I have an shopware6 admin extension where some custom entity is stored and loaded. This custom entity has a ManyToManyAssociation to another custom entity. In my mask I use a entity multi select component for the selection.
const category = this.categoryRepository.create();
category.categoryId = this.newCategory;
category.weight = this.newWeight;
category.certificates = this.certificateCollection;
this.categoryRepository.save(category).then(() => {
this.isLoading = false;
this.isSaveSuccessful = true;
this.getList();
}).catch((exception) => {
this.isLoading = false;
this.createNotificationError({
message: this.$tc(
'global.notification.notificationSaveErrorMessageRequiredFieldsInvalid'
)
});
throw exception;
});
<sw-entity-multi-select
:label-property="labelPropertyCertificate"
:criteria="certificateCriteria"
entity-name="certificate"
:entity-collection="certificateCollection"
#change="changeCertificates"
/>
When sending the save request the content of my certificate selection is send with and js object with the property id. To create the correct entity I added some Entity creation process and build my own entity collection for the categoryCertificate collection.
Like this
const category = this.categoryRepository.create();
category.categoryId = this.newCategory;
category.weight = this.newWeight;
category.certificates = new EntityCollection(
this.categoryCertificateRepository.route,
this.categoryCertificateRepository.entityName,
Context.api,
);
this.certificates.forEach(function(item){
const categoryCertificate = me.categoryCertificateRepository.create();
categoryCertificate.categoryId = category.id;
categoryCertificate.certificateId = item.id;
category.certificates.add(categoryCertificate);
});
console.log(category);
this.categoryRepository.save(category).then(() => {
this.isLoading = false;
this.isSaveSuccessful = true;
this.getList();
}).catch((exception) => {
this.isLoading = false;
this.createNotificationError({
message: this.$tc(
'global.notification.notificationSaveErrorMessageRequiredFieldsInvalid'
)
});
throw exception;
});
The console.log shows this, which would be correct
categoryId: "77b959cf66de4c1590c7f9b7da3982f3"
certificates: r(1)
0: Proxy
[[Handler]]: Object
[[Target]]: Object
categoryId: "aa26daab83b04cc6af9a525e7ec1ce16"
certificateId: "8c32fb76000844bcb4d850831fe2f6c1"
extensions: {}
_isNew: true
[[Prototype]]: Object
[[IsRevoked]]: false
...
The request payload shows this, which is not correct.
{id: "b96c923dcee941c7a05c0cb4f98de4d9", categoryId: "251448b91bc742de85643f5fccd89051", weight: 0,…}
categoryId: "251448b91bc742de85643f5fccd89051"
certificates: [{id: "10fb19bccdb0419ca5f4b7abe6561db2"}]
id: "b96c923dcee941c7a05c0cb4f98de4d9"
weight: 0
where are my categoryId & certificateId properties gone? Does the categoryRepository strip them out?
Edit solution
Thx to dneustadt
this.certificates.forEach(function(item){
const categoryCertificate = me.careHintCategoryCertificateRepository.create();
categoryCertificate.id = item.id;
category.certificates.add(categoryCertificate);
});
this works!
By using a ManyToManyAssociationField the values for the columns of the mapping table are resolved dynamically. Since these columns belong to the mapping definition, not to your actual CategoryCertificate entity, they get stripped. You don't need to provide these IDs when you use the respective ManyToManyAssociationField property to assign data from either side.
Related
Im trying to make a shopping cart using react redux. i can add products to my shopping cart but have no idea how to remove a product from my cart.
i tried to remove by splice method but it doesnt seem to work.
Heres my cartRedux -
import {createSlice} from '#reduxjs/toolkit';
const cartSlice = createSlice({
name: "cart",
initialState: {
products:[],
quantity:0,
total:0
},
reducers:{
addProduct: (state, action) => {
state.quantity += 1;
state.products.push(action.payload);
state.total += action.payload.price * action.payload.quantity;
},
removeProduct: (state, action) => {
let index = state.products.indexOf(action.payload);
state.quantity -= action.payload
state.products.splice(index, 1)
}
},
});
export const {addProduct} = cartSlice.actions;
export default cartSlice.reducer;
Replace the array instead of its content.
Changing the line :
state.products.slice(index, 1)]
by
state.products.splice(index, 1)
state.products = [...state.products] // clone array
should allow redux to notice the change.
Receives the item ID as a payload which is then used to remove from the state using the filter method.
const removeItem = state.products.filter((item) => item.id !== action.payload);
state.products = removeItem;
removeProduct: (state, action) =>{state.products.indexOf(action.payload);
state.products.splice(action.payload, 1)};
There is a problem with my work. since Firebase's Web/JavaScript API always returns the full tree under the nodes that we request.
So in my case i retrieved all of existing fields from firebase including sensitive fields first and after that I want to export to excel selected fields only, not all of the fields that i got. the problem is, I always succeed exported all existing fields, including the sensitive fields.
Can I export selected field only and exclude the sensitive field? Below is my code:
I retrieve all of my fields include the data from firebase in my .ts file like this:
getData() {
this.dataLoading = true;
this.querySubscription = this._backendService.getDocs('report')
.subscribe(members => {
this.members = members;
this.dataSource = new MatTableDataSource(members);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
},
(error) => {
this.error = true;
this.errorMessage = error.message;
this.dataLoading = false;
},
() => { this.error = false; this.dataLoading = false; });
}
//export func
exportAsXLSX():void{
this._backendService.exportAsExcelFile(this.members, 'sample');
}
My Backend service Code :
getDocs(coll:string,filters?:any){
this.itemsCollection=this.afs.collection<any>(this.getCollectionURL(coll));
return this.itemsCollection.valueChanges();
}
getCollectionURL(filter){
return "ReportApp/fajar/"+filter;
}
//export func
public exportAsExcelFile(json: any[], excelFileName: string): void {
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] };
const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
this.saveAsExcelFile(excelBuffer, excelFileName);
}
private saveAsExcelFile(buffer: any, fileName: string): void {
const data: Blob = new Blob([buffer], {type: EXCEL_TYPE});
FileSaver.saveAs(data, fileName + '_export_' + new Date().getTime() + EXCEL_EXTENSION);
}
as for reference im using code from here to exporting to excel :https://medium.com/#madhavmahesh/exporting-an-excel-file-in-angular-927756ac9857
as u can see I put all of my data into this.member variable and export em, But the result is that I exported all of em, i want to export selected fields only.
You will need to "trim down" the array of member data before you send it to your exportAsExcelFile() method. Your problem is that you are passing ALL of the member data to that export function. So the solution is to remove any sensitive information before you call the export function.
exportAsXLSX():void {
// TRIM DOWN ARRAY HERE
this._backendService.exportAsExcelFile(this.members, 'sample');
}
Since you didn't provide your member database structure, or details of what you consider sensitive information, I'll provide a generic example. You have an array of members... Most likely, you've made each "member" in the array into an object... so we need to loop over that array and delete the "sensitive" property of each member object.
As a precaution, since we don't want to delete the properties from the ACTUAL array, since arrays are reference-types, and since you might need those details elsewhere... let's make a copy of the array - a deep copy to ensure even nested objects are copied.
var newMemberArray = JSON.parse(JSON.stringify(this.members))
Then, we need to loop over that new array and delete our sensitive properties:
newMemberArray.forEach(function(m){
delete m.sensitivePropertyName1;
delete m.sensitivePropertyName2;
});
and pass that "sanitized" array to your export function... so putting all this together, something like:
exportAsXLSX():void {
var newMemberArray = JSON.parse(JSON.stringify(this.members))
newMemberArray.forEach(function(m){ delete m.sensitivePropertyName });
this._backendService.exportAsExcelFile(newMemberArray, 'sample');
}
*Disclaimer: untested code, for explanation purposes only
I want to construct a object base on an array and another object.
The goal is to send to mailchimp api my users interests, for that, I've got :
//Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
//List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
//Mapping of user skill to all skills
const outputSkills = skillsUser1.map((skill) => skillsMailchimpId[skill]);
console.log(outputSkills);
The problem is after, outputSkill get me an array :
["ID1", "ID3"]
But what the mailchimp api need, and so what I need : :
{ "list_id_1": true,
"list_id_2": false, //or empty
"list_id_3" : true
}
A simple way would be this (see comments in code for explanation):
// Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
// List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
// Create an output object
const outputSkills = {};
// Use `Object.entries` to transform `skillsMailchimpId` to array
Object.entries(skillsMailchimpId)
// Use `.forEach` to add properties to `outputSkills`
.forEach(keyValuePair => {
const [key, val] = keyValuePair;
outputSkills[val] = skillsUser1.includes(key);
});
console.log(outputSkills);
The basic idea is to loop over skillsMailchimpId instead of skillsUser.
But that is not very dynamic. For your production code, you probably want to refactor it to be more flexible.
// Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
// List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
// Use `Object.entries` to transform `skillsMailchimpId` to array
const skillsMailchimpIdEntries = Object.entries(skillsMailchimpId);
const parseUserSkills = userSkills => {
// Create an output object
const outputSkills = {};
// Use `.forEach` to add properties to `outputSkills`
skillsMailchimpIdEntries.forEach(([key, val]) => {
outputSkills[val] = userSkills.includes(key);
});
return outputSkills;
}
// Now you can use the function with any user
console.log(parseUserSkills(skillsUser1));
I'm trying to encapsulate a TextInput such that when the value changes it does a serverside lookup and based on the result shows a notification to the user: "That group name already exists". I've been using this as my example to start from: https://marmelab.com/admin-on-rest/Actions.html#the-simple-way
My current error is
Error: The TextInput component wasn't called within a redux-form . Did you decorate it and forget to add the addField prop to your component? See https://marmelab.com/admin-on-rest/Inputs.html#writing-your-own-input-component for details.
but even if I add in
NameLookupTextInput.defaultProps = {
addField: true, // require a <Field> decoration
}
I still get the error. Heres my code
class NameLookupTextInput extends TextInput {
handleChange = eventOrValue => {
console.log("handleChange",eventOrValue);
this.props.onChange(eventOrValue);
this.props.input.onChange(eventOrValue);
if(this.timeoutHandle){
clearTimeout(this.timeoutHandle);
}
console.log(fetch);
this.timeoutHandle = setTimeout(function(){
let value = this.props.input.value;
fetchUtils.fetchJson(API_ENDPOINT+'/groupNameCheck/'+value, { method: 'GET'})
.then((data) => {
console.log(data);
let exists = data.json.exists;
let name = data.json.name;
if(exists){
console.log(this.props.showNotification('The group name "'+name+'" already exists.'));
}else{
console.log(this.props.showNotification('The group name "'+name+'" does not exist.'));
}
})
.catch((e) => {
console.error(e);
//showNotification('Error: comment not approved', 'warning')
});
}.bind(this),500);
};
}
export default connect(null, {
showNotification: showNotificationAction
})(NameLookupTextInput);
I'm new in CRM development. I know a basic thing like "best practice for crm 2011"
I wanna understand now how to work with lookup fields. And I think I chose the easiest way for my self.
I have an costum entity "contract" it has 5 more field, 2 of these are lookups.
First lookup (agl_contractId) - it is a link by it self
Second lookup (agl_ClientId) - link to Client.
What do I need?
When I choose fill First lookup (agl_contractId), script should find in this contract a Client and copy-past it to current form.
I've done script but it isn't work... (((
function GetAccountFromContract()
{
XrmServiceToolkit.Rest.Retrieve(Xrm.Page.getAttribute("agl_osnovnoy_dogovorid").getValue(),
'agl_osnovnoy_dogovoridSet',
null,null,
function (result) {
var Id = Xrm.Page.getAttribute("agl_osnovnoy_dogovorid").getValue();
if (result.Id != null) {
var LookupData = new Array();
var LookupItem = new Object();
var lookuptextvalue = lookupvalue[0].name;
var lookupid = lookupvalue[0].id;
var lokupType = lookupvalue[0].entityType;
alert(lookupvalue);
alert(lookupData);
Xrm.Page.getAttribute("agl_accountid").setValue(lookupData);
}
},
function (error) {
equal(true, false, error.message);
},
false
);
}
If I understand you well: When you select Contract in agl_osnovnoy_dogovorid field, you want to pull Client property from that Contract and put it in agl_accountid field?
If that is right:
First, get Id of selected Contract (from agl_osnovnoy_dogovorid field)
var selectedContract = new Array();
selectedContract = Xrm.Page.getAttribute("agl_osnovnoy_dogovorid").getValue();
{
var guidSelectedContract = selectedContract[0].id;
//var name = selectedContract[0].name;
//var entType = selectedContract[0].entityType;
}
Second, retrieve Client from agl_osnovnoy_dogovorid. Your oData query will be like:
http://crmserver/org/XRMServices/2011/OrganizationData.svc/ContractSet(guid'" + guidSelectedContract + "')/CustomerId
(In example I'm using CustomerId field. For your case enter Schema Name of Client field).
Now, execute query and put result into agl_accountid field:
$.getJSON(
Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc/ContractSet(guid'" + guidSelectedContract + "')/CustomerId",
function(data){
if(data.d.CustomerId != null && data.d.CustomerId.Id != null && data.d.CustomerId.Id != "undefined")
{
//set agl_accountid field
Xrm.Page.getAttribute("agl_accountid").setValue([{id:data.d.CustomerId.Id, name:data.d.CustomerId.Name, typename:data.d.CustomerId.LogicalName}]);
}
});
Your using REST to retrieve data but also using FetchXml example to setup the agl_accoutid lookup.
Also some of the conditions are not clear … anyway … I’ve incorporated the change to your original post.
function GetAccountFromContract()
{
var aodLookupValue = Xrm.Page.getAttribute("agl_osnovnoy_dogovorid").getValue();
if (!aodLookupValue) return;
XrmServiceToolkit.Rest.Retrieve( aodLookupValue[0].id ,
'agl_osnovnoy_dogovoridSet', null,null,
function (result) {
var customer = result.d["your attribute name"];
if (customer) {
var LookupData = new Array();
var LookupItem = new Object();
var lookuptextvalue = customer.Name;
var lookupid = customer.Id;
var lokupType = customer.LogicalName;
alert(lookupvalue);
alert(lookupData);
Xrm.Page.getAttribute("agl_accountid").setValue(lookupData);
}
},
function (error) {
equal(true, false, error.message);
}, false );
}