Greetings fellow Pimcore devs, I'm working in on a project that runs on Pimcore X ! But I'm running agains some "inconveniences". When I try to reach my controller I get the error message:
"App\Foo\Bar\Controller\SomeController" has no container set, did you forget to define it as a service subscriber?
let me show some code snippets:
src\Foo\Bar\Controller\SomeController
namespace App\Foo\Bar\Controller;
/**
* #Route("/admin/Bar")
*/
class SomeController extends AdminController
{
/**
* #Route("/foo", name="foo-you-too")
*
* #return Response
*/
public function someAction(): Response
{
die('howdy!');
}
}
src\Foo\Bar\DependencyInjection\BarBundleExtension
namespace App\Foo\Bar\DependencyInjection;
class BarBundleExtension extends ConfigurableExtension implements PrependExtensionInterface
{
/**
* {#inheritdoc}
*/
public function loadInternal(array $config, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
src/Foo/Bar/Resources/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: false
#Foo\Bar\Controller\:
#App\Bar\Controller\:
App\Foo\Bar\Controller\:
resource: '../../Controller'
public: true
autowire: true
autoconfigure: true
tags: ['controller.service_arguments']
src/Foo/Bar/Resources/config/pimcore/routing.yml
app:
resource: "#Bar/Controller/"
type: annotation
src/Foo/Bar/Resources/public/js/startup.js
pimcore.registerNS("pimcore.plugin.menusample");
pimcore.plugin.menusample = Class.create(pimcore.plugin.admin, {
getClassName: function () {
return "pimcore.plugin.menusample";
},
initialize: function () {
pimcore.plugin.broker.registerPlugin(this);
this.navEl = Ext.get('pimcore_menu_search').insertSibling('<li id="pimcore_menu_mds" class="pimcore_menu_item pimcore_menu_needs_children">Howdy there!</li>', 'after');
this.menu = new Ext.menu.Menu({
items: [{
text: "Some text",
iconCls: "pimcore_icon_apply",
handler: function (button) {
new Ext.Window({
title: t('-'),
width: '50%',
height: '80%',
layout: 'fit',
items: [
{
xtype: "component",
autoEl: {
tag: "iframe",
src: "/admin/Bar/foo"
},
border: false
}
]
}).show();
}.bind(this)
}],
cls: "pimcore_navigation_flyout"
});
pimcore.layout.toolbar.prototype.mdsMenu = this.menu;
},
pimcoreReady: function (params, broker) {
var toolbar = pimcore.globalmanager.get("layout_toolbar");
this.navEl.on("mousedown", toolbar.showSubMenu.bind(toolbar.mdsMenu));
pimcore.plugin.broker.fireEvent("mdsMenuReady", toolbar.mdsMenu);
}
});
const menusamplePlugin = new pimcore.plugin.menusample();
I suspect there is something wrong with my services.yml, I already looked for similar StackOverflow questions but to no avail
I found the solution the name of the DependencyInjection class name was wrong it should be the BarExtension and not BarBundleExtension like in the namespace.
Related
I can't get this code to work.
import "reflect-metadata";
export class Castable {
[key: string]: any;
constructor(source: any) {
console.log("source: ");
console.log(source);
Object.getOwnPropertyNames(source).forEach((propertyKey) => {
const designType = Reflect.getMetadata("design:type", this, propertyKey);
const customType = Reflect.getMetadata("custom:type", this, propertyKey);
const type = customType !== undefined ? customType : designType;
this[propertyKey] = this.convert(
source[propertyKey],
propertyKey,
type,
0
);
});
console.log("after constructor this: ");
console.log(this);
}
private convert(
source: any,
propertyKey: string,
type: any,
depth: number
): any {
if (type === undefined) {
return source;
}
switch (type.name) {
case "Number":
return Number(source);
case "String":
return String(source);
case "Boolean":
return source.toString() === "true";
default:
return new type(source);
}
}
}
/** --- TreeRoot --- */
export class MyConfigRoot extends Castable {
result: boolean;
count: number;
}
function init() {
const json = '{"result":true, "count":32}';
let input = JSON.parse(json);
let newR = new MyConfigRoot(input);
console.log("after new: ");
console.log(newR);
}
init();
After getting the type with Reflect.getMetadata, type checking is performed.
This code would result in an empty new object.
> node ./dist/test.js
source:
{ result: true, count: 32 }
after constructor this:
MyConfigRoot { result: true, count: 32 }
after new:
MyConfigRoot { result: undefined, count: undefined }
The assignment in constractor seems to succeed, but actually comes up empty.
It is actually deeper than that, but it is a minimal structure to isolate the problem. Why would it be empty?
This is caused by the useDefineForClassFields compilerOptions, or the lack of it. Because if not manually set, its default value is correlated to target option. From docs:
This flag is used as part of migrating to the upcoming standard version of class fields. TypeScript introduced class fields many years before it was ratified in TC39. The latest version of the upcoming specification has a different runtime behavior to TypeScript’s implementation but the same syntax.
This flag switches to the upcoming ECMA runtime behavior.
Default:
true if target is ES2022 or higher, including ESNext, false otherwise.
Feel free to read the detailed backstory. Short explainer to your case goes like this:
/** --- TreeRoot --- */
class MyConfigRoot extends Castable {
result: boolean; // 👈
count: number;
}
These type-only annotations in TS used to be harmless and have no JS runtime effect. However when useDefineForClassFields is introduced, the compilation result changes.
useDefineForClassFields: true
/** --- TreeRoot --- */
class MyConfigRoot extends Castable {
constructor() {
super(...arguments);
Object.defineProperty(this, "result", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "count", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
}
useDefineForClassFields: false
/** --- TreeRoot --- */
class MyConfigRoot extends Castable {
}
Thus the behavior you observed.
Besides tuning TS compiler option, you can also use the declare keyword in your TS v3.7+ code to fine control the JS compilations.
/** --- TreeRoot --- */
class MyConfigRoot extends Castable {
declare result: boolean; // 👈 declare keyword
count: number;
}
// Now `useDefineForClassFields: true` COMPILES TO:
/** --- TreeRoot --- */
class MyConfigRoot extends Castable {
constructor() {
super(...arguments);
Object.defineProperty(this, "count", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
}
I am unable to add an existing object to my game
function create() {
const scene: Phaser.Scene = this
// scene.physics.add.image(400, 100, 'ball') THIS WORKS => BALL APEARS IN GAME
const ball = new Phaser.GameObjects.Image(scene, 400, 100, 'ball')
scene.physics.add.existing(ball) // nothing displays
}
What am I missing out on ?
I had the same problem, but then discovered I had to add it to both the scene and the physics manager, such as:
const ball = new Phaser.Physics.Arcade.Sprite(scene, 400, 100, 'ball');
this.add.existing(ball);
this.physics.add.existing(ball);
var config = {
width: 800,
height: 600,
type: Phaser.AUTO,
loader: {
baseURL: 'https://raw.githubusercontent.com/nazimboudeffa/assets/master/',
crossOrigin: 'anonymous'
},
parent: 'phaser-example',
physics: {
default: 'arcade'
},
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload()
{
this.load.image('dude', 'sprites/phaser-dude.png')
}
function create ()
{
var dude = new Phaser.GameObjects.Sprite(this, 100, 100, 'dude')
this.add.existing(dude)
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>
I'm trying to build a dynamic wizard with react-native-navigation v2.
I say dynamic because the number of steps may vary depending on which options the user selects.
I was thinking to use nested stacks, so the layout of my app would look something like this:
{
root: {
sideMenu: {
left: {/*...*/},
center: {
stack: {
children: [component1, /*...,*/ componentX]
}
}
}
}
}
ComponentX is where I start my wizard, so I push a new stack like this:
{
// ...
stack: {
children: [
component1,
//...,
componentX,
{
stack: {
children: [step1, step2, /*...,*/ stepN]
}
}
]
}
}
After the user make the last choice on stepN, I would like to replace the nested stack with a summary screen to have something like:
{
//...
stack: {
children: [
component1,
//...,
componentX,
summaryScreen
]
}
}
I could use Navigation.setRoot to reset the whole thing but that means I would probably have to store the navigation in Redux.
I've also tried using Navigation.setStackRoot but I'm under the impression that it's setting the parent stack root and not my nested stack...
I've finally managed to solve it.
Here's how:
At the start of my app, when I give an id to my main stack
const sideMenu = {
left: { /*...*/ },
center: {
stack: {
id: 'main', // this line is important
children: [/*...*/]
}
},
};
Navigation.setRoot({
root: { sideMenu },
});
When I want to start my wizard, I push a new stack
Navigation.push(componentId, {
stack: {
id: 'wizard',
children: [
{
component: { /*...*/ },
},
],
}
})
I push the screens on the new stack wizard as the user progresses
When I want to display the final summary screen, I call setStackRoot on the nested stack
Navigation.setStackRoot('wizard', [
{
component: { /*...*/ },
},
]);
On that summary screen, I have a button labelled 'Finish' which removes the nested stack
Navigation.pop('main');
EDIT: with this approach alone, if you click on the back arrow when you are on a nested screen, it will dismiss the entire nested stack instead of this screen only.
I had to use a custom back button as follows:
I solved it by using a custom back button:
1. When pushing a new screen where I want to override the button, use the options
import Icon from 'react-native-vector-icons/MaterialIcons';
/* ... */
const backIcon = await Icon.getImageSource('arrow-back', 24, '#000');
const component = {
id: screenID,
name: screenID,
passProps,
options: {
topBar: {
leftButtons: [
{
id: 'backButton',
icon: backIcon,
},
],
},
}
};
return Navigation.push(componentId, { component });
Create a HOC to implement you custom back action
import React, { Component } from 'react';
import { Navigation } from 'react-native-navigation';
const getDisplayName = WrappedComponent => WrappedComponent.displayName || WrappedComponent.name || 'Component';
export default function withCustomBackButton(WrappedComponent) {
class WithCustomBackButton extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
if (this.navigationEventListener) this.navigationEventListener.remove();
}
navigationButtonPressed() {
// Your custom action
const { componentId } = this.props;
Navigation.pop(componentId);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
WithCustomBackButton.displayName = `WithCustomBackButton(${getDisplayName(WrappedComponent)})`;
return WithCustomBackButton;
}
When registering the screen with the custom back button, wrap it in your HOC
import withCustomBackButton from '../components/hoc/WithCustomBackButton';
/* ... */
Navigation.registerComponent('selectLocation', () => withCustomBackButton(SelectLocation));
Has any one figure out on how to load the server data from the ng2-smart table plugin of Angular2.
I have few products data that is retrieved from Node API and Im able to display the same onClick event in the browser log.
I need to display the same in this 3rd party plugins table area which they have provided in this documentation below:
Frontend : https://akveo.github.io/ng2-smart-table/#/examples/populate-from-server
Under "Server Data Source Example"
Code: https://github.com/akveo/ng2-smart-table/blob/master/src/app/pages/examples/server/advanced-example-server.component.ts
Accordingly i have configured in my code as below:
blank-page.component.ts
import { ServerDataSource } from 'ng2-smart-table';
import { Component } from '#angular/core';
import { Http } from '#angular/http';
#Component({
selector: 'advanced-example-server',
template: `
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
`,
})
export class BlankPageComponent implements OnInit {
settings = {
columns: {
id: {
title: 'ID',
},
albumId: {
title: 'Album',
},
title: {
title: 'Title',
},
url: {
title: 'Url',
},
},
};
source: ServerDataSource;
//Doubt or Problem here!!!
constructor(http: Http) {
this.source = new ServerDataSource(http, { endPoint: 'https://jsonplaceholder.typicode.com/photos' });
}
//Tried like this too (Which is not the right way of calling)
constructor(http: Http) {
this.source = new ServerDataSource(http, { endPoint: this.productService.getProductsOncategory(this.categoryid) });
}
//Dint work this too!!
constructor(http: Http) {
this.source = new ServerDataSource(http, { endPoint:'http://localhost:5000/products/getProductsOncategory ' });
}
}
Where my service.ts file is like, which actually displays the products data in my browser log which i need to show in my table data
getProductsOncategory(category_id){
let catUrl="http://localhost:5000/products/getProductsOncategory"
let headers = new Headers();
headers.append('Content-Type','application/json');
let catIdObj = JSON.stringify({category_id:category_id})
console.log(catIdObj)
return this.http.post(catUrl,catIdObj,{headers:headers})
.map((response:Response)=>response.json())
.do(data=>console.log(JSON.stringify(data)))
.catch(this.handleError);
}
Error if i use my projects url in endpoint
Error: Uncaught (in promise): Error: Data must be an array. Please check that data extracted from the server response by the key '' exists and is array.
This is what i did and worked perfect for me, i used smart table server side paging, but build my own filter for custom filtration experience.
1- Define Server Data Source
source: ServerDataSource;
2- set it in constructor with config object
this.source = new ServerDataSource(http,
{
endPoint: 'full-url-for-endpoint',
dataKey: 'your-list-path-from-response' for example 'data.records' ,
pagerPageKey: 'your backend param excpected for page number key',
pagerLimitKey: 'your backend param excpected for page size',
totalKey: total records returned in response path for example 'data.total',
filterFieldKey: your filter keys template should set to '#field#' if you need to send params as you set, Default is '#field#_like'
});`
3- add settings object
settings = {
actions: {
custom: [ // if you need custom actions do like that
{ name: 'view-something', title: '<i title="Exception" class="nb-alert"></i>' },
{ name: 'do-custom', title: '<i class="fa fa-pencil"></i>' }
],
add: true, //if you don't need default add button set to false
edit: true, //if you don't need default add button set to false
delete: true, //if you don't need default delete button set to false
position: 'right' // buttons position
}, // remove add , edit , delete objects if you don't use
add: {
addButtonContent: '<i class="nb-plus"></i>',
createButtonContent: '<i class="nb-checkmark"></i>',
cancelButtonContent: '<i class="nb-close"></i>',
},
edit: {
editButtonContent: '<i class="nb-edit"></i>',
saveButtonContent: '<i class="nb-checkmark"></i>',
cancelButtonContent: '<i class="nb-close"></i>',
},
delete: {
deleteButtonContent: '<i class="nb-trash"></i>',
confirmDelete: true,
},
pager: {
display: true // set to false if no need for pagination
},
columns: {
Id: { // set up table cols - Id is a prop name returned from backend
title: 'ID', // display name in table header
type: 'number',
filter: false // add text filter for it or not
},
Name: {
title: 'Full Name',
type: 'string',
filter: false
}
}
};
// Add Filter Data , i used a custom form binded with ngModel above table for filtration, so assume you have a model called filter which get data from external form
FilterData() {
this.source.reset(true); // reset your old filtered data
this.source.setPage(1, false); // set page to 1 to start from beginning
let filterArr = this.getFilterArray(); // add a new filter data, but be careful to not sent any empty data, as it throws an exception
if (filterArr.length)
this.source.setFilter(filterArr, false, false);
this.source.refresh(); // this will call the server with new filter and paginatio data
}
getFilterArray() { // setup new filter
let filterArray = [];
if (this.filter.id)
filterArray.push({ field: 'id', search: this.filter.id });
if (this.filter.name)
filterArray.push({ field: 'name', search: this.filter.name});
return filterArray;
}
onCustomAction(event) { // custom buttons code
switch (event.action) {
case 'view-something':
// put your code here
break;
default:
console.log('Not Implemented Action');
break;
}
}
With this example my data is resource so the datakey is set resource
find below sample code
{
source: ServerDataSource;
constructor(http: HttpClient) {
this.source = new ServerDataSource(http, { dataKey: 'resource', endPoint:'http://localhost:8080/api/v2/mysql/_table/able' })
}
You need to set the dataKey for the ServerDataSource. For example, if your JSON is { data: [...], total: .. }, you need to set dataKey = 'data'.
this worked for me on Angular 8, But Search box functionality needs to be handeled from backend (i.e: localhost:5000/session_info/Seance1?temp_like=30), so backend need to filter (temp_like = value) in database, which make search box retrive live data.
Here is the entire component with Edit and Delete, Enjoy it!
import {Component} from '#angular/core';
import {ServerDataSource} from 'ng2-smart-table';
import {HttpClient} from "#angular/common/http";
import {Setting} from "../../setting";
#Component({
selector: 'ngx-session-man',
templateUrl: './sessions-man.component.html',
styleUrls: ['./sessions-man.component.scss'],
})
export class SessionsManComponent {
settings = {
mode: 'inline',
add: {
addButtonContent: '<i class="nb-plus"></i>',
createButtonContent: '<i class="nb-checkmark"></i>',
cancelButtonContent: '<i class="nb-close"></i>',
},
edit: {
editButtonContent: '<i class="nb-edit"></i>',
saveButtonContent: '<i class="nb-checkmark"></i>',
cancelButtonContent: '<i class="nb-close"></i>',
confirmSave: true,
},
delete: {
deleteButtonContent: '<i class="nb-trash"></i>',
confirmDelete: true,
},
columns: {
name: {
title: 'Séance',
type: 'string',
},
start: {
title: 'Début',
type: 'any',
},
end: {
title: 'Fin',
type: 'any',
},
},
};
source: ServerDataSource;
constructor(private httpClient: HttpClient) {
this.source = new ServerDataSource(this.httpClient, {endPoint: Setting.baseUrl + 'all_sessions_table'});
}
onDeleteConfirm(event): void {
if (window.confirm('Are you sure you want to delete ' + event['data']['name'] + '?')) {
event.confirm.resolve();
this.httpClient.delete<any>('http://localhost:5000/del-seance/' + event['data']['name']).subscribe(
temps => {});
} else {
event.confirm.reject();
}
}
onEditConfirm(event): void {
if (window.confirm('Are you sure you want to edit ' + event['data']['name'] + '\'s name to ' + event['newData']['name'] + '?')) {
event.confirm.resolve();
this.httpClient.post<any>('http://localhost:5000/mod-seance/' + event['data']['name'] + '/' + event['newData']['name'], { title: 'Session deleted' }).subscribe(
temps => {});
} else {
event.confirm.reject();
}
}
}
this my Controller:
public class ProductEntityController : EntitySetController<
ProductEntity, int>
{
public IQueryable< ProductEntity> Get(ODataQueryOptions< ProductEntity> parameters)
{
return productList.AsQueryable();
}
public ProductEntity Create(ProductEntity entity)
{
productList.Add(entity);
return entity;
} }
//---------------------------------------
this my JS code:
var context = new $data.initService('/odata');
context.then(function (db) {
var dsD = db.ProductEntity.asKendoDataSource();
grid= $('#gridD').kendoGrid({
dataSource: dsD,
filterable: true,
sortable: true,
pageable: true,
selectable: true,
height: 400,
columns: [
{ field: 'Name' },
{ field: 'Created' },
{ field: 'Index' },
{ field: 'LargeNum' },
{ command: ["edit", "destroy", "update"] }
],
toolbar: ["create", "save", "cancel"],
editable: "inline"
}).data("kendoGrid");
}).fail(function (args) { });
//------
when i "Add New Record" or "Save Changes" , two request are sent to the server(GET and then POST).
i have an error :'result count failed ' at GET Response.
i found a problem in following code in kendo.js:
create: function (options, model) {
var query = self;
query.entityContext.onReady().then(function () {
if (model.length > 1) {
..............
..........
}
else {
console.dir(ctx.storeToken);
model[0]
.innerInstance() // when i comment this line everything is gonna be ok
.save(ctx.storeToken)
.then(function () {
options.success();
})
.fail(function () {
console.log("error in create");
options.error({}, arguments);
});
}
});
}
why is called innerInstance() before save?
how can i fix my problem?
i use Jaydata 1.3.6 with kendo ui and webApi2 Odata on MVC 5
please help me
this problem occurred when i changed default value of key Field(Id) in create mode at Client side. JayData send GET request with filter to the server.therefore i had and error because i didn't have this Id on my entity.then send POST new Entity to the server.
conclusion:don't have permission to edit your key in kendoGrid and handle it at server.