I'm very new to JavaScript MVC logic. I'm trying to create a simple app.
I've set up everything like in the example (In seperate files ofc). Data.js which is not shown contains an object with some data, and template.js contains my html template strings.
// CONTROLLER
import Model from './models/model';
import View from './views/view';
import Controller from './controllers/controller';
import { $on } from './utility/utility';
export default class Controller {
constructor(Model, View) {
this.model = Model;
this.view = View;
};
loadInit() {
console.log("controller render");
this.model.test();
// this.view.render(this.model.data);
};
}
// MODEL
import data from '../config/data'
data.initialize();
export default class Model {
constructor() {
this.data = data;
};
test() {
console.log("Test works");
};
generateLists() {
window.infiniteList = [];
var articlesList = 'http://lib.lrytas.lt/api/articlesList/article.php?term=/lrytas/' + data.blockTag.slug + '/*&domain=' + data.blockTag.domain;
$.get(articlesList, function(response) {
for(let i = 0; i < response.blockTop7.length; i++) {
if (response.blockTop7[i].n18 == "0") {
window.infiniteList.push(response.blockTop7[i].main_id);
}
};
for(let i = 0; i < response.newBlock.length; i++) {
if (response.newBlock[i].n18 == "0") {
window.infiniteList.push(response.newBlock[i].main_id);
}
};
});
console.log(infiniteList);
};
loadArticle(data) {
$.get(lr_config.kolumbusApi + 'query/?kpm3id=' + infiniteList[0] + '&ret_fields=props', function(response) {
let result = response.result["0"].props;
this.data.update(result);
});
window.infiniteList.splice(0,1);
};
}
// VIEW
import config from '../config/config'
import Template from '../template/template'
import data from '../config/data'
data.initialize();
export default class View {
constructor() {
this.el = config.mainWrapper;
};
render(Data) {
console.log("view render");
this.el.innerHTML += Template(Data);
};
}
// APP.JS
class App {
constructor() {
this.model = new Model();
this.view = new View();
this.controller = new Controller(Model, View);
};
}
const app = new App();
const init = () => {
app.controller.loadInit();
}
$on(window, 'load', init);
Heres JSBIN.
Now the problem is when I'm trying to call init function on load in main.js. It goes to controller, calls the loadInit() function which then should call this.model.test(), but it wouldnt work and it gives me the following error
"Uncaught TypeError: this.model.test is not a function".
I've been trying to find a solution for past couple hours but I'm really lost here.
Any help would be greatly appreciated. Thanks!
Related
I am trying to integrate Opentelemetry (Otl) in my Angular application to trace the frontend calls. Everything works fine and I am able to see the calls in the Zipkin.
But the only problem is that it is showing it as "unknown_service" in the Zipkin interface.
Below is my entire Angular code and Zipkin screenshot as well. This is just a sample application. But my requirement is that I am going to integrate the Opentelemetry code in the http interceptor so that it will be easy to maintain at one place instead of every service call. Also service.name should be passed dynamically so that it will be traced in Zipkin.
How can I add a service name before it gets called?
import { Component, OnInit } from '#angular/core';
import {ZipkinServicesService} from './zipkin-services.service';
// Opentelemetry components
import { context, trace } from '#opentelemetry/api';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '#opentelemetry/tracing';
import { WebTracerProvider } from '#opentelemetry/web';
import { XMLHttpRequestInstrumentation } from '#opentelemetry/instrumentation-xml-http-request';
import { ZoneContextManager } from '#opentelemetry/context-zone';
import { CollectorTraceExporter } from '#opentelemetry/exporter-collector';
import { B3Propagator } from '#opentelemetry/propagator-b3';
import { registerInstrumentations } from '#opentelemetry/instrumentation';
import { ZipkinExporter } from '#opentelemetry/exporter-zipkin';
#Component({
selector: 'app-zipkin-integration',
templateUrl: './zipkin-integration.component.html',
styleUrls: ['./zipkin-integration.component.scss']
})
export class ZipkinIntegrationComponent implements OnInit {
respData: string;
webTracerWithZone;
constructor(
public zipkinService: ZipkinServicesService,
) {
const providerWithZone = new WebTracerProvider();
const options = {
url: 'http://localhost:9411/api/v2/spans',
serviceName: 'interceptor-example',// This is NOT working.
}
const exporter = new ZipkinExporter(options);
const zipKinProcessor = new SimpleSpanProcessor(exporter);
providerWithZone.addSpanProcessor(zipKinProcessor);
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new CollectorTraceExporter()));
providerWithZone.register({
contextManager: new ZoneContextManager(),
propagator: new B3Propagator(),
});
registerInstrumentations({
instrumentations: [
new XMLHttpRequestInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
'https://httpbin.org/post',
],
}),
],
});
this.webTracerWithZone = providerWithZone.getTracer('example-tracer-web');
}
ngOnInit(): void {
}
zipGet (){
let i = 10;
const span1 = this.webTracerWithZone.startSpan(`files-series-info-${i}`);
let postData = [{
no : 2,
emp : 3
}];
context.with(trace.setSpan(context.active(), span1), () => {
this.zipkinService.httpGet(postData).subscribe( (data: any) => {
this.respData = data;
// Opentelemetry after response.
trace.getSpan(context.active()).addEvent('fetching-span1-completed');
span1.end();
});
});
}
zipPost (){
let postData = [{
no : 1,
emp : 2
}];
let i = 10;
const span1 = this.webTracerWithZone.startSpan(`files-series-info-${i}`);
context.with(trace.setSpan(context.active(), span1), () => {
this.zipkinService.httpPost(postData).subscribe( (data: any) => {
this.respData = data;
// Opentelemetry after response.
trace.getSpan(context.active()).addEvent('fetching-span1-completed');
span1.end();
});
});
}
}
Service name must be set via resource as per the specification. I am not sure which version of js libs you are using. This should get you the service name.
import { Resource } from '#opentelemetry/resources';
import { ResourceAttributes } from '#opentelemetry/semantic-conventions'
...
...
const provider = new WebTracerProvider({
resource: new Resource({
[ResourceAttributes.SERVICE_NAME]: "interceptor-example"
}),
});
use providerConfig to set service name. follow code set service name to "SPA Test".
import { Resource } from '#opentelemetry/resources';
import { SemanticResourceAttributes } from '#opentelemetry/semantic-conventions'
import { BatchSpanProcessor } from '#opentelemetry/sdk-trace-base';
import { WebTracerProvider } from '#opentelemetry/sdk-trace-web';
import { ZipkinExporter, ExporterConfig } from '#opentelemetry/exporter-zipkin';
const providerConfig = {
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "SPA Test"
}),
};
const provider = new WebTracerProvider(providerConfig);
const zipkinOptions: ExporterConfig = {
url: "http://localhost:9411/api/v2/spans"
};
const exporter = new ZipkinExporter(zipkinOptions);
const zipkinProcessor = new BatchSpanProcessor(exporter);
provider.addSpanProcessor(zipkinProcessor);
provider.register();
var tracer = provider.getTracer(CustomersComponent.name, "0.1.0");
var span = tracer.startSpan(CustomersComponent.name);
console.info(span);
span.end();
I am facing an issue with typescript build while trying to wrapper multiple classes into a single function.
Below is the sample code.
// AppState.ts
export class AppState {
static id: string;
}
// AppLogic.ts
import { AppState } from './AppState'
export class AppLogic{
constructor(id : string){
AppState.id = id;
}
public getAppID() : string{
return AppState.id;
}
}
// main.ts
import { AppLogic } from './AppLogic';
export function AppwrapAPI(id : string): any{
class AppAPI{
private app : AppLogic;
constructor(id : string){
this.app = new AppLogic(id);
}
public getAppID() : string{
return this.app.getAppID();
}
}
return new AppAPI(id);
}
<!DOCTYPE html>
<html>
<head>
<title>Test app</title>
</head>
<body>
<script type="module">
import { AppwrapAPI } from "./dist/main.es.js";
let a1 = new AppwrapAPI("123");
alert(a1.getAppID()); ///"123"
let a2 = new AppwrapAPI("321"); ///"321"
alert(a2.getAppID());
alert(a1.getAppID()); ///"321" <<--- wrong data
</script>
</body>
</html>
The above ts project generates the following js code using rollup built.
var AppState = /** #class */ (function () {
function AppState() {
}
return AppState;
}());var AppLogic = /** #class */ (function () {
function AppLogic(id) {
AppState.id = id;
}
AppLogic.prototype.getAppID = function () {
return AppState.id;
};
return AppLogic;
}());function AppwrapAPI(id) {
var AppAPI = /** #class */ (function () {
function AppAPI(id) {
this.app = new AppLogic(id);
}
AppAPI.prototype.getAppID = function () {
return this.app.getAppID();
};
return AppAPI;
}());
return new AppAPI(id);
}export{AppwrapAPI};//# sourceMappingURL=main.es.js.map
In this code, the AppwrapAPI function wraps only the AppAPI function instead of all the functions and the AppState function is outside the AppwrapAPI function which creates an issue when I create multiple instances for the AppwrapAPI function.
let a1 = new AppwrapAPI("123");
alert(a1.getAppID()); ///"123"
let a2 = new AppwrapAPI("321"); ///"321"
alert(a2.getAppID());
alert(a1.getAppID()); ///"321" <<--- wrong data
I want to wrapper all the three classes AppState, AppLogic, AppAPI inside the AppwrapAPI function so that the AppState is not shared between the multiple instances.
Similar to the below code
"use strict";
function AppwrapAPI(id) {
class AppState {
}
class AppLogic {
constructor(id) {
AppState.id = id;
}
getAppID() {
return AppState.id;
}
}
class AppAPI {
constructor(id) {
this.app = new AppLogic(id);
}
getAppID() {
return this.app.getAppID();
}
}
return new AppAPI(id);
}
let a1 = AppwrapAPI("123");
console.log(a1.getAppID()); /// output : 123
let a2 = AppwrapAPI("321");
console.log(a2.getAppID()); /// output : 321
console.log(a1.getAppID()); /// output : 123
Right now I manually modifying the generated code to avoid this issue but it gives me some other issue while debugging with the source map generated from the old file.
Can anyone suggest to me how to modify the typescript code to get the single function that wraps all the classes in the typescript project?
I am in the process of converting a AngularJS app to Angular 7 in TypeScript.
I am having some problems with converting some complex nested promises to Observables.
Here is an example of the code I'm dealing with:
signup.component.js
function SomethingSignupController(somethingApplication) {
function activate() {
getApplication();
}
function getApplication() {
vm.getFromServer = false;
vm.promises = [];
SomethingRepo.get().then(function(application) {
vm.getFromServer = true;
vm.application = application;
vm.promises.push(Something.getCompany().then(function(company) {
vm.company = company;
if (vm.company.structure === ‘more_25’) {
return SomethingRepo.getAllOwners().then(function(owners) {
vm.owners = owners;
for(var i = 0; i < vm.owners.length; i++) {
vm.promises.push(getOwnerFiles(vm.owners[i]));
}
}
}
}
vm.promises.push(SomethingRepo.getSomethingOne().then(function(somethingOne) {
vm.somethingOne = somethingOne;
}
vm.promises.push(SomethingRepo.getSomethingTwo().then(function(somethingTwo) {
vm.somethingTwo = somethingTwo;
}
vm.promises.push(SomethingRepo.getSomethingThree().then(function(somethingThree) {
vm.somethingThree = somethingThree;
}
/* and a few more like the above */
$q.all(vm.promises).then(function(){
postGet();
}).finally(function() {
vm.promises = [];
});
}
}
function postGet() {
/* does something with the data acquired from SomethingRepo */
}
/* when an application is send */
function send() {
somethingApplication.promises = [];
somethingApplication.errors = [];
if (vm.getFromServer) {
update();
} else {
create();
}
}
function update() {
somethingApplication.promises.push(SomethingRepo.update(vm.application).then(angular.noop, function(error) {
somethingApplication.parseErrors(error, ‘Some error’);
}));
patchInfo();
}
function create() {
}
function patchInfo() {
somethingApplication.promises.push(SomethingRepo.patchAccount(vm.account).then(angular.noop, function(error) {
somethingApplication.parseErrors(error, ‘Account error: ‘);
}
/* a few more patches */
$q.all(somethingApplication.promises).then(function() {
/* display dialog */
}, angular.noop).finally(function() {
postGet();
somethingApplication.promises = [];
if (somethingApplication.errors.length >= 1) {
vm.errors = somethingApplication.errors;
}
});
}
}
somethingApplication.service.js
function somethingApplication(SomethingRepo) {
var promises = [], errors = [];
var service = {
promises: promises;
errors = errors;
parseErrors: parseErrors;
};
return service;
function parseErrors(error, base_string) {
angular.forEach(error.data.erros, function(value_params, key_params) {
this.errors.push(base_string + ‘ ‘ + key_params.replace(/_/g, ‘ ‘) + ‘ ‘ + value_params);
}, this);
}
}
somethingRepo.js
function SomethingRepo(Server) {
function get() {
return Server.get(‘/something/application’, null, {noGlobal: true});
}
}
I have reduced the files, but they consist of more code like this.
The point of the controller is to create or update an application for another website. On my website I have a form of fields corresponding to the form on the other website. If you already have filed for an application, but want to update it, the info you already filed are loaded from the other website.
The problem is, in order to create or update an application, a lot of different endpoints requested og posted to.
In AngularJS I store the promises from each request and run them asynchronously in the end. In TypeScript and Angular I want to use Observables and subscribe to the data change.
How do I get started? How do I subscribe to an Observable the requires parameters from another Observable? Any advice how to proceed?
Here's an example demonstrating how you can easily use observables in your scenario -
Your service would be something like this -
import { Injectable } from '#angular/core';
import { AppConstants } from '../../app.constants';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class ExampleService {
constructor(private appconstants: AppConstants, private http: HttpClient) { }
get() {
return this.http.get(this.appconstants.apiUrl);
}
getSomethingOne() {
return this.http.get(this.appconstants.apiUrl1);
}
getSomethingTwo() {
return this.http.get(this.appconstants.apiUrl2);
}
}
Then simply use it in your component as follows -
import { Component } from '#angular/core';
import { forkJoin } from 'rxjs';
import { ExampleService } from '../services/example.service';
#Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
data;
dataOne;
dataTwo;
constructor(private exampleService: ExampleService) { }
getApplication() {
const combined = forkJoin([
this.exampleService.get(),
this.exampleService.getSomethingOne(),
this.exampleService.getSomethingTwo()
]);
combined.subscribe(res => {
this.data = res[0];
this.dataOne = res[1];
this.dataTwo = res[2];
});
}
}
var app = angular.module('testApp', []);
class Component {
constructor(app, name, template, as, bindings) {
this.bindings = bindings;
this.config = {}
this.config.template = template;
this.config.controllerAs = as;
// pre-create properties
this.config.controller = this.controller;
this.config['bindings'] = this.bindings;
app.component(name, this.config);
console.log("Inside Component ctor()");
}
addBindings(name, bindingType) {
this.bindings[name] = bindingType;
}
controller() {
}
}
class App extends Component {
constructor(app) {
var bindings = {
name: "<"
};
super(app, "app", "Hello", "vm", bindings);
}
controller() {
this.$onInit = () => this.Init(); // DOESN'T WORK
/*
var self = this;
self.$onInit = function () { self.Init(); }; // DOESN'T WORK
*/
/*
this.$onInit = function () { // WORKS
console.log("This works but I don't like it!");
};
*/
}
Init() {
console.log("Init");
}
onNameSelected(user) {
this.selectedUser = user;
}
}
var myApp = new App(app);
<div ng-app="testApp">
<app></app>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.js"></script>
I'm trying to "classify" angular 1.5's .component(). I can get most of it figured out but when I try to assign a class method for $onInit it doesn't work. I've tried assigning to it and using arrow notation to call back to the class method but neither work. It does work if I assign an anonymous function directly but I don't want to do that. I want those functions to point to class methods because I find it cleaner.
So ultimately I want my App classes Init() method to get called for $onInit(). Is it possible?
i want to share data between components, so im implemented a Service which has an EventEmitter.
My Service looks like this:
#Injectable()
export class LanguageService {
constructor() {
this.languageEventEmitter = new EventEmitter();
this.languages = [];
this.setLanguages();
}
setLanguages() {
var self = this;
axios.get('/api/' + api.version + '/' + api.language)
.then(function (response) {
_.each(response.data, function (language) {
language.selected = false;
self.languages.push(language);
});
self.languageEventEmitter.emit(self.languages);
})
.catch(function (response) {
});
}
getLanguages() {
return this.languages;
}
toggleSelection(language) {
var self = this;
language.selected = !language.selected;
self.languages.push(language);
self.languageEventEmitter.emit(self.languages);
}
}
I have to components, which are subscribing to the service like this:
self.languageService.languageEventEmitter.subscribe((newLanguages) => {
_.each(newLanguages, function (language) {
self.updateLanguages(language);
});
});
When both components are loaded, the language arrays get filled as i wish.
This is the first component:
export class LanguageComponent {
static get parameters() {
return [[LanguageService]];
}
constructor(languageService) {
var self = this;
this.languageService = languageService;
this.languages = [];
this.setLanguages();
}
setLanguages() {
var self = this;
self.languageService.languageEventEmitter.subscribe((newLanguages) => {
_.each(newLanguages, function (language) {
self.updateLanguages(language);
})
});
}
updateLanguages(newLanguage) {
var self = this;
if (!newLanguage) {
return;
}
var match = _.find(self.languages, function (language) {
return newLanguage._id === language._id;
});
if (!match) {
self.languages.push(newLanguage);
}
else {
_.forOwn(newLanguage, function (value, key) {
match[key] = value;
})
}
toggleLanguageSelection(language) {
var self = this;
self.languageService.toggleSelection(language)
}
}
When LanguageComponent executes the function toggleLanguageSelection() which triggered by a click event, the other component, which subscribes like this:
self.languageService.languageEventEmitter.subscribe((newLanguages) => {
_.each(newLanguages, function (language) {
self.updateLanguages(language);
})
});
doesn't get notfiefied of the change. I think this happens because both component get a different instance of my LanguageService, but i'm not sure about that. I also tried to create a singleton, but angular'2 di doesn't work then anymore. What is the reason for this issue and how can i solve this ?
You need to define your shared service when bootstrapping your application:
bootstrap(AppComponent, [ SharedService ]);
and not defining it again within the providers attribute of your components. This way you will have a single instance of the service for the whole application. Components can leverage it to communicate together.
This is because of the "hierarchical injectors" feature of Angular2. For more details, see this question:
What's the best way to inject one service into another in angular 2 (Beta)?