I take out the logic in the service separately from the component.
There is a property in the logic, on which the display of the element in the component depends
The property is declared like this:
function serviceAbonent () {
...
const switchAdd = ref(false)
...
return { switchAdd }
}
In the component, I get this property like this:
const { stitchAdd }= serviceAbonent()
In this method, I call the service method, which changes the value of the property
function newItemAdd () {
serviceAbonent().add(newItemBlack, 'Black', 'CreateDate')
}
service method:
async function add (newItem :any, list: string, defaultSort: string) {
switchAdd.value = false ...
The problem is that when the value changes, the value only changes in the service, but does not change in the component. Why is that ?
Related
I am trying to get into stimulusJS
import { Controller } from 'stimulus'
export default class extends Controller {
static targets = [
'foo',
]
connect() {
const fooValue = this.fooTarget.value
console.log(this.fooValue) // 7
this.someFunction()
}
someFunction(){
console.log(this.fooValue) // undefined
}
}
I want to be able to get this value on connect as I want to know if it has changed.
Your code declares const variable within the scope of connect() function. But you should use this (Stimulus Controller) property instead:
...
connect() {
this.fooValue = this.fooTarget.value
...
I have the following method in my Vuex action:
const actions = {
async fetchByQuery({ commit, title }) {
console.log(title);
//other codes
},
};
And method to reach to vuex action:
methods: {
...mapActions(["fetchByQuery"]),
getData(title) {
console.log("teacher");
this.fetchByQuery(title);
}
}
But the console.log() from action is giving undefined output in the console.
What am I missing here ??
You got the parameters inside your action wrong.
({ commit, title }) has to be ({ commit }, title)
Otherwise you would have to call it with an object with a property title.
Vuex actions expect two parameters, the context object { commit } and the payload (title, in your case)
Change your action declaration to this:
const actions = {
async fetchByQuery({ commit }, title) {
console.log(title);
//other codes
},
};
Assume data has already been cached in sessionStorage. I have hydrateStateWithSessionStorage in an external CacheService.js file. I import this file. When I try to pass this.setState to this function, I get this error:
Uncaught TypeError: Cannot read property 'updater' of undefined
How can I solve this? I could possibly use a the React hook useState and pass the setter function, but what if I want to use a class component instead of functional component? Or am I simply unable to pass setState because it implicitly uses the 'this' keyword in its implementation?
hydrateStateWithSessionStorage(state, setState) {
// for all items in state
for (let key in state) {
// if the key exists in localStorage
if (sessionStorage.hasOwnProperty(key)) {
// get the key's value from localStorage
let value = sessionStorage.getItem(key);
// console.log(value)
// parse the localStorage string and setState
try {
value = JSON.parse(value);
console.log('before')
setState({ [key]: value });
console.log('after')
} catch (e) {
// handle empty string
setState({ [key]: value });
}
}
}
}
//in the component consuming CacheService
//this.Cache = new CacheService(); //in the constructor
componentDidMount() {
this.Cache.hydrateStateWithLocalStorage(this.state, this.setState);
this.Auth.fetch('api/upcomingbill/').then((data) => {
this.setState({ list: data })
});
}
I would treat this function more of a check return sessionStorage object if nothing return undefined. Send this.state into the fn() then check response and return the response.
componentDidMount() {
const newState = hydrateState(this.state):
!!newState && this.setState(newState)
}
Just a brain dump..
How about this?
componentDidMount() {
this.Cache.hydrateStateWithLocalStorage(this);
this.Auth.fetch('api/upcomingbill/').then((data) => {
this.setState({ list: data })
});
}
And then using setState like so...
hydrateStateWithSessionStorage(ReactComp) {
for (let key in ReactComp.state) {
...
ReactComp.setState({ [key]: value });
....
}
}
FYI, this is not a React specific issue. The reason you are getting that error is, you are passing just a method that internally uses "this" (of that particular class) to the hydrateStateWithSessionStorage function. By the time, that method is called in the function, the scope has changed and "this" is not defined or not the same anymore.
I'm using adonisjs, and am trying to sanitize a post request on the server. The data I get back has extra properties that are not mapped to the table/model so it is erroring when I try to save. Here is the update method
async update ({ params, request, response }) {
const contract = await Contract.find(params.id);
contract.merge(request.post());
return await contract.save();
}
The problem is that when I returned the contract earlier on a get request, I added some computed properties. I could do something along the lines of
const { prop1, prop2 } = request.post();
but that doesn't feel like a future proof or clean solution. Ideally I want the object to only have the properties defined on the table/model. I have a validator setup as described in the validator docs, but it still lets other properties bypass it.
I resolved this by adding a beforeSave hook in the model class that filter's properties on the object, which allows us to keep a thin controller.
const FIELDS = ['id', 'description'];
class Contract extends Model {
static boot() {
super.boot();
this.addHook('beforeSave', async contractInstance => {
Object.keys(contractInstance.$attributes).forEach(key => {
if (!CONTRACT_FIELDS.includes(key)) {
delete contractInstance.$attribute[key];
}
});
});
}
}
What about an helper class?
class RequestContractExtractor{
static desiredProps = [
"prop1",
"prop2",
]; // you could replace this with a list you get from your model class
constructor(requestData){
desiredProps.forEach(prop => this[prop] = requestData[prop]);
}
static from(...args){ return new this(...args); }
}
Then you'd be able to do:
async update ({ params, request, response }) {
const contract = await Contract.find(params.id);
contract.merge(RequestContractExtractor.from(request.post()));
return await contract.save();
}
I resolved this by adding a beforeSave hook that filter's properties on the object.
Create a file at /app/Model/Hooks/ContractHook.js
'use strict'
const ContractHook = module.exports = {}
const CONTRACT_FIELDS = ["id", "description"];
ContractHook.removeDynamicFields = async (contractInstance) => {
if (contractInstance) {
Object.keys(contractInstance.$attributes).forEach((key) => {
if (!CONTRACT_FIELDS.includes(key) && contractInstance.$attributes[key]) {
delete contractInstance.$attributes[key];
}
});
}
};
Use it in your model like so...
class Contract extends Model {
static boot() {
super.boot();
this.addHook("beforeCreate", [
"ContractHook.removeDynamicFields",
]);
}
I have issue, with passing async data to child component. I trying to write dynamic form generator. Issue starts when I try to call json via Observable and pass it into child component.
service:
generateSearchFields2(): Observable<any> {
return this.http
.get(this.API + 'searchFields')
.map((res:Response) => {
res.json().data as any;
for (var i = 0; i < res.json().data.length; i++) {
var searchField = res.json().data[i];
switch (searchField.component) {
case "TextboxQuestion":
let TXQ: TextboxQuestion = new TextboxQuestion({
key: searchField.key,
label: searchField.label,
value: searchField.value,
required: searchField.required,
order: searchField.order
});
this.searchFieldModels.push(TXQ);
console.log("TXQ: ", TXQ, this.searchFieldModels);
break;
case "DropdownQuestion":
let DDQ: DropdownQuestion = new DropdownQuestion({
key: searchField.key,
label: searchField.label,
required: searchField.required,
options: searchField.options,
order: searchField.order
});
this.searchFieldModels.push(DDQ);
console.log("TXQ: ", DDQ, this.searchFieldModels);
break;
default:
alert("DEFAULT");
break;
}
}
return this.searchFieldModels.sort((a, b) => a.order - b.order);
})
.catch(this.handleError);
}
Component Parent:
generateSearchFields2() {
this.service.generateSearchFields2()
.subscribe(res => this.searchFields = res)
}
Iam passing variable via INPUT directive in parent template to child: [searchFields]="searchFields"
Issue is in child component, where searchField has undefined value. In this child I pass value to another service, to create formContros, but I got undefined there also. Data missing starts here, in child:
#Input() searchFields: SearchBase<any>[] = [];
ngOnInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
Please for hint how I can pass async variable, to not loose data meantime
You can make #Input() searchFields a setter
private _searchFields: SearchBase<any>[] = [];
#Input() set searchFields(value SearchBase<any>[]) {
if(value != null) {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
}
get searchFields() : SearchBase<any>[] {
return this.searchFields;
}
You can also use ngOnChanges() which is called every time an input is updated, but a setter is usually more convenient except perhaps when the executed code depends on multiple inputs being set.
In the ngOnInit event the data which comes from the parent is not bound yet. So your searchFields is undefined yet. You can use it in NgAfterViewInit component lifecycle event.
#Input() searchFields: SearchBase<any>[] = [];
ngAfterViewInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
For other cases you can see Angular2 Component Lifecycle events