I have a Data-Import-Module with data-import.service, data-import component, and its child components: uploader.component(with its own service defined in uploader module) save.component(button) and display-table.component (button) and table.component.
I want to upload csv file(which I imagine as table with points, Point(x;y;)), save it to the server using httpClient.post method passing with body{fileName, content-of-csv-parsed to-json or just a table}, display a table with content of uploded file.
httpClient methods are new to me and i really don't understand what for are headers and param which are third (options) argument in post method- should i put there fileName or should i put it body with Point[]? Can body have more than one parameter? Should i create File(fileName:string, points: Point[]) and put is as body?
export class DataImportService {
constructor (private http: HttpClient){}
baseUrl: string="api/files";
postFile(fileName:string, points: Point[] ):Observable<any>{
return this.http.post(this.baseUrl,{fileName,points} ){};
}
vs
export class DataImportService {
constructor (private http: HttpClient){}
baseUrl: string="api/files";
postFile(file:File ):Observable<File>{
return this.http.post(this.baseUrl,file ){};
}
These are my two conceptons, i really want to understand :
DataImportComponent{
constructor(private dataService: DataImportService)
saveFile(data.csv){
...
return this.dataService.dataService.post(...).subscribe(
x=>{parsed to json});
)
}
}
Related
my name is DP, I have 2 years Vue2 experience, but I am new to Vue3. I am learning Vue3 recently, as I found the "setup(Composition API)" just like the "Controller(in MVC)" that I did in other language, so I am trying to build my test Vue3 project in MVC way, but I go some problem can anyone help? thx!
MVC Plan
M - use class
V - use <template> ... </template>
C - use setup
My Problem
working: using loadTopic_inSetup().then() in setup is working, because topicList_inSetup is defined in setup() too.
not working: using loadTopic_inModel() in setup is not working, I guess some kind data keep problem, because in console I can see the data already got from API
as u can see, I am not expert for js/ts, I am a backend developer, so if you know how to do it, plz help thx very much.
BTW, VUE is greet, I love it.
My Code
//APIBased.ts
import { ajax } from "#/lib/eeAxios"
export class APIBased {
//load data with given url and params
loadData(apiPath: string, params?: object): Promise<any> {
apiPath = '/v1/'+apiPath
return ajax.get(apiPath, params)
}
}
//Topic.ts
import { APIBased } from "./APIBased";
import { ref } from 'vue'
export class Topic extends APIBased {
//try keep data in model
topicList: any = ref([]);
constructor() {
super()
}
//direct return ajax.get, let setup do the then+catch
loadTopic_inSetup() {
return super.loadData('topics', { t_type_id: 1 })
}
//run ajax get set return data to this.topicList, keep data in model
loadTopic_inModel() {
super.loadData('topics', { t_type_id: 1 }).then((re) => {
console.log(re.data)
this.topicList = re.data
})
}
}
//EETest.vue
<template>
<EELayoutMainLayout>
<template v-slot:mainContent>
<h1>{{ "Hello Vue3 !!" }}</h1>
<hr/>
{{to.topicList}} //not working... just empty array
<hr/>
{{topicList_inSetup}} //working... topic list return from API show here.
</template>
</EELayoutMainLayout>
</template>
<script lang="ts">
import { defineComponent, getCurrentInstance, ref } from 'vue'
import EELayoutMainLayout from '#/components/eeLayout/EELayoutMainLayout.vue'
import { Topic } from "#/models/Topic";
export default defineComponent({
name: 'EETest',
props: {
},
setup() {
let topicList_inSetup = ref([])
const to = new Topic()
//try keep data in setup, it's working
to.loadTopic_inSetup().then((re) => {
topicList_inSetup.value = re.data
console.log(re.data)
})
//try keep data in model, the function is run, api return get, but data not show, even add ref in model
to.loadTopic_inModel()
return {
topicList,
to,
}
},
components: {
EELayoutMainLayout,
},
})
</script>
A few digressions before solving the problem. Maybe you are a java developer. I personally think it is inappropriate to write the front end with Java ideas. The design of vue3's setup is more inclined to combined functional programming
To fully understand why you need some pre knowledge, Proxy and the get and set method of Object
They correspond to the two core apis in vue, reactive and ref,
The former can only be applied to objects( because proxy can only proxy objects),The latter can be applied to any type(primary for basic javascript types, get and set can apply for any type)
You can modify the code to meet your expectations
loadTopic_inModel() {
super.loadData('topics', { t_type_id: 1 }).then((re) => {
console.log(re.data)
this.topicList.value = re.data
})
}
You cannot modify a ref object directly, a test case to explain what is reactive
when ref function is called, a will be like be wrapped in a class has value properties, and has get and set method
the effect function will call the arrow function, and in this time, the get method of a will be called and it will track as a dependence of the effect function, when a changed, the set method of a will be called, and it will trigger the arrow function,
so when you direct modify the a, the setter method will never trigger, the view will not update
const a = ref(1)
let dummy
let calls = 0
effect(() => {
calls++
dummy = a.value
})
expect(calls).toBe(1)
expect(dummy).toBe(1)
a.value = 2
expect(calls).toBe(2)
expect(dummy).toBe(2)
// same value should not trigger
a.value = 2
expect(calls).toBe(2)
I have a service that requests data from a get method, I'd like to map the response to an object storing some Ids and use those Ids to make other http requests.
I was told this isn't usually done in a callback manner, I looked at this How do I return the response from an asynchronous call? but I don't think it's the usual way to implement services, any hints are very appreciated.
Tried adding in onInit/constructor method in angular to be sure the object was filled before other methods were called without success.
#Injectable ()
export class ContactService {
storeIds;
getIds(callback: Function) {
this.http.get<any>(IdsUrl, Config.options).subscribe(res => {
callback(response);
});
getIds(res => {
this.storeIds = {
profileId: res.profile,
refIds: res.refIds
}
}
)
// this.storeIds returns undefined as it's an async call
this.http.post<any>(WebserviceUrl + this.storeIds.profileId , data, headers )
// .....Many other web services that relay on this Ids
}
Just create another service called StoreIdsService. Update the response you get from your first api call 'getIds' in the StoreIdsService. The idea is to have StoreIdsService as singleton service to keep state of your storeIds. You can inject StoreIdsService in anywhere component you want to get the storeIds.
Its one of manyways to share data in angular between components.
Please refer to this answer someone has posted.
How do I share data between components in Angular 2?
You can simply assign the service response to the storeIds property inside the subscribe method. and call the subsequent services inside it if you need.
#Injectable ()
export class ContactService {
storeIds;
getIds() {
this.http.get<any>(IdsUrl, Config.options).subscribe(res => {
this.storeIds = {
profileId: response.profile,
refIds: response.refIds
}
this.otherapicall1();
this.otherapicall2();
});
}
I've monkey-patched my router to store the current route components in a session variable:
var Router = Ember.Router.extend({
customSession: Ember.inject.service('session-custom'),
location: config.locationType,
didTransition: function() {
this._super(...arguments);
this.get('customSession').set('currentEntity', this.get('currentRouteName').split('.')[0]);
this.get('customSession').set('currentDetailView', this.get('currentRouteName').split('.')[1]);
}
});
I know that this is not the cleanest of solutions, but writing the session to the console proves that at least those parameters are set.
In my controller, I'd like to listen for changes in these parameters, but somehow this does not work:
import Ember from 'ember';
import ApplicationController from './application';
export default ApplicationController.extend({
customSession: Ember.inject.service('session-custom'),
currentRouteNameChanged: Ember.observer('customSession.currentEntity', function () {
console.log("route changed");
})
});
i.e. "route changed" is never printed to the console.
This seems quite an easy fix, but I haven't been able to find a solution on SO.
Thanks!
Perhaps consider using an initializer to inject your session-custom service into your application’s controllers. To get there, some suggestions…
First, in the Router, and elsewhere, use the conventional, camelized short-hand to inject your service, like this:
sessionCustom: Ember.inject.service(),
...and be sure to reference sessionCustom in your code, instead of customSession.
Next, create a session-custom initializer, and inject the service into your application’s controllers:
export function initialize(application) {
application.inject('controller', 'sessionCustom', 'service:session-custom');
}
export default {
name: 'session-custom',
initialize,
};
Observing route changes from the controller should now be successful:
export default Ember.Controller.extend({
currentRouteNameChanged: Ember.observer(
'sessionCustom.currentEntity', function() {
console.log("CONTROLLER: ", this.get('sessionCustom.currentEntity'));
}
),
});
These changes, of course, can also be observed from the service itself:
export default Ember.Service.extend({
currentEntity: '', // <-- it's helpful to explicitly declare
currentRouteNameChanged: Ember.observer(
'currentEntity', function() {
console.log("SERVICE: ", this.get('currentEntity'));
}
),
});
I’ve created an Ember Twiddle to demonstrate this solution.
I am trying to navigate to a specific url using location.go service from typescript function. It changes the url in the browser but the component of the url is not reflected in the screen. It stays on the login (actual) screen - say for eg:
constructor(location: Location, public _userdetails: userdetails){
this.location = location;
}
login(){
if (this.username && this.password){
this._userdetails.username = this.username;
this.location.go('/home');
}
else{
console.log('Cannot be blank');
}
}
Is there a compile method or a refresh method I am missing?
Yes, for redirecting from one route to another you should not be using location.go, it generally used to get normalized url on browser.
Location docs has strictly mentioned below note.
Note: it's better to use Router service to trigger route changes. Use
Location only if you need to interact with or create normalized URLs
outside of routing.
Rather you should use Router API's navigate method, which will take ['routeName'] as you do it while creating href using [routerLink] directive.
If you wanted redirect via URL only then you could use navigateByUrl which takes URL as string like router.navigateByUrl(url)
import {
ROUTER_DIRECTIVES,
RouteConfig,
ROUTER_PROVIDERS,
Location, //note: in newer angular2 versions Location has been moved from router to common package
Router
} from 'angular2/router';
constructor(location: Location,
public _userdetails: userdetails,
public _router: Router){
this.location = location;
}
login(){
if (this.username && this.password){
this._userdetails.username = this.username;
//I assumed your `/home` route name is `Home`
this._router.navigate(['Home']); //this will navigate to Home state.
//below way is to navigate by URL
//this.router.navigateByUrl('/home')
}
else{
console.log('Cannot be blank');
}
}
Update
In further study, I found that, when you call navigate, basically it does accept routeName inside array, and if parameters are there then should get pass with it like ['routeName', {id: 1, name: 'Test'}].
Below is API of navigate function of Router.
Router.prototype.navigate = function(linkParams) {
var instruction = this.generate(linkParams); //get instruction by look up RouteRegistry
return this.navigateByInstruction(instruction, false);
};
When linkParams passed to generate function, it does return out all the Instruction's required to navigate on other compoent. Here Instruction means ComponentInstruction's which have all information about Routing, basically what ever we register inside #RouteConfig, will get added to RouteRegistry(like on what path which Component should assign to to router-outlet).
Now retrieved Instruction's gets passed to navigateByInstruction method, which is responsible to load Component and will make various thing available to component like urlParams & parent & child component instruction, if they are there.
Instruction (in console)
auxInstruction: Object //<- parent instructions
child: null //<- child instructions
component: ComponentInstruction //<-current component object
specificity: 10000
urlParams: Array[0] <-- parameter passed for the route
urlPath: "about" //<-- current url path
Note: Whole answer is based on older router version, when Angular 2 was in beta release.
I'm trying to split an ExtJS4 application into modules.
- app
- store
- controller
- model
- view
- module
- m1
- model
- view
- controller
- m2
- model
- ...
The problem is, when I start the application and it inits one of the m1 controllers, the controller has no this.control() function.
-- edit --
I defined a class inside of the controller folder.
Ext.define( 'App.module.ping.controller.Ping', {
extend: 'Ext.app.Controller',
requires: [
'App.module.ping.view.PingPanel'
],
init: function() {
this.control( {
'#app.module.ping': {
render: this.onPanelRendered
}
} );
},
onPanelRendered: function() {
...
}
} );
Later I call
Ext.create('App.module.ping.controller.Ping').init();
but I get this error:
Uncaught TypeError: Cannot call method 'control' of undefined
Ext.define.control./lib/extjs-4.1.0/src/app/Controller.js:414
Ext.define.init./app/module/ping/app/controller/Ping.js:11
Ext.define.init./app/module/ping/app/Module.js:11
(anonymous function)app.js:17
(anonymous function)app.js:35
Module.js is the file with the create() call
Ping.js is the file with the define() call
-- edit2 --
Pathological example:
input:
Ext.define( 'MyController', {
extend: 'Ext.app.Controller',
init: function() { console.log('initialized'); }
});
output:
function constructor() {
return this.constructor.apply(this, arguments);
}
input:
Ext.create('MyController');
output:
constructor
input:
MyController.create();
output:
constructor
-- edit3 --
The controllers require the application object in the config object when created. The application object adds itself to all the controllers, which are not manually created with create. It calls something like this:
Ext.create('App.controller.Users', { application: this, ... } );
Later it uses the application object to redirect the control call to it.
control: function ( config ) { this.application.control( config ) };
So I'm probably gonna implement some mechanism, which adds the application to those controllers automatically, when I create them.
Have you tried
var pingController = Application.getController('App.module.ping.controller.Ping');
pingController.init(); //or pingController.init(Application);
where Application is a reference to the object created during Ext.application.launch - such as
var Application = {};
Ext.application({
//all of your settings,
launch:function(){
Application = this;
//other launch code
}
}
});
To anyone else who comes here searching for a solution to:
Uncaught TypeError: Cannot call method 'control' of undefined
Day long frustration aside, the above answer did not solve my problem, but lead me to try the following, which did fix the issue:
// app.js
Ext.application({
...
launch: function(){
var me = this;
...
me.getController('ControllerName');
}
});
// inside controller/ControllerName.js
init: function(app){
var me = this;
app.control({
'#editButton': {
click: function(){
me.someFunction();
}
}
});
},
That app variable is the same as the "var Application = {}" that's in the chosen answer for the question -- meaning I didn't need to add it globally (or at all). I must've tried a million different things, but this finally worked. Hope it saves someone else.
Don't need to call init() manually. Just call Ext.create and constructor will be called automatically.