How to dynamically control what menu items are shown - skyve

In my Skyve application, I have a situation where - whether users see an application menu item in the responsive mode needs to be determined dynamically - based on some combination of preference settings etc - rather than merely what module role they have been assigned.
How can I dynamically add or remove items from the menu they access and where do I locate this code?

(NOTE: The usual Skyve no-code approach relies on access to menu items being controlled by the roles declared in the module, and so overriding the menu class is not usually required - this is definitely an advanced activity.)
The responsive mode in Skyve (which is using PrimeFaces) uses a the "Menu" session scoped faces view bean to generate what is seen by users, and this is referenced from the various template.xhtml page templates. You can extend this class to control what elements of the menu are see by users.
Your project includes the template.xhtml pages for whichever theme you have chosen to use in your project - for example if you are using the editorial (free) theme, then the corresponding template will be at:
src/main/webapp/WEB-INF/pages/editorial/template.xhtml
You'll find the reference to the Menu bean around line 155 (depending on your particular Skyve version) and it will be similar for other themes.
<p:panelMenu id="leftMenu" model="#{menu.menu}" />
Extend org.skyve.impl.web.faces.beans.Menu to customise the behaviour of the menu. Probably the simplest approach is to define all potential menus in the usual way - by declaring these in the "module.xml" file - and then adding or removing the items that are not applicable in your Menu class override.
For example, if your module has the following menu items declared:
<menu>
<edit document="MyDetail" name="My Details">
<role name="StaffUser"/>
</edit>
<edit document="OrganisationDashboard" name="Organisation Dashboard">
<role name="Manager"/>
<role name="Administrator"/>
</edit>
<list query="qAllStaff" name="All Staff">
<role name="Manager"/>
<role name="Administrator"/>
</list>
...
To conditionally hide the "Organisation Dashboard" menu item - then you can create your own Menu override class as follows:
package com.myOrg.faces;
import java.util.Iterator;
import java.util.List;
import javax.faces.bean.ManagedBean;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.DefaultSubMenu;
import org.primefaces.model.menu.MenuElement;
import org.primefaces.model.menu.MenuModel;
import org.skyve.impl.web.faces.beans.Menu;
#ManagedBean(name = "myCustomMenu")
public class MyCustomMenu extends Menu {
#Override
public MenuModel getMenu() {
DefaultMenuModel result = (DefaultMenuModel) super.getMenu(); // do the usual steps Skyve will do according to the no-code module declaration
List<MenuElement> subMenus = result.getElements();
if (! subMenus.isEmpty()) {
DefaultSubMenu mainMenu = (DefaultSubMenu) subMenus.get(0);
mainMenu.getElements().removeIf(e -> {
return ((e instanceof MenuItem) &&
"Organisation Dashboard".equals((MenuItem) e).getValue());
});
}
return result;
}
}
Then you can provide this bean name in the template.xhtml as follows:
<p:panelMenu id="leftMenu" model="#{myCustomMenu.menu}" />

Related

Create one navbar and use it on different pages with different style

Can I create one navbar and use it on different pages with different style in angular ?
On the first page the دnavbar is transparent and appears after scrolling, but I want it to be white and sticky in the rest of the other pages.
You can check url and add different navigation components based on current route.
constructor(private router: Router ) {
console.log(this.router.url); //route name
}
If you have a navbar in the main component, then you can update its sticky behavior with 2 approaches:
Approach 1 (Preferred way):
Let's say that you already have many pages where you want your navbar to be sticky or transparent. Going to each page & changing some configs is time-consuming.
Instead, you subscribe to Router's NavigationEnd event in app.component.ts
constructor(private router: Router) {}
setNavSticky=true; //Default to true if we have many pages
ngOnInit() {
this.router.events.pipe(
filter(ev=>ev instanceof NavigationEnd)
).subscribe((navEndEvent:NavigationEnd)=>{
if(navEndEvent.url.includes('/home')){
//Add more pages in condition for which navbar should not be sticky
this.setNavSticky=false;
}
else{
this.setNavSticky=true;
}
})
}
Bind the sticky class in the app.component's HTML:
<nav class="navbar" [class.sticky]="setNavSticky">
<header>My main navbar</header>
...
</nav>
Approach 2: Create a utility service at the root level which has the property isNavbarSticky. All the pages that want to set it false, can do so by injecting it in their component file.
With approach 2, you have to manually set/unset isNavbarSticky in pages that don't want sticky navbar.

toLocaleDateString is not a function when called dynamically in Angular runtime

I have an Array of Elements of the type ActionInstruction.
Some get added in the constructor of the component, others are added dynamically by the user via a dialog form field.
actionInstructions: ActionInstruction[];
constructor(private _formBuilder: FormBuilder, public dialog: MatDialog) {
this.actionInstructions = [{post_date_start: new Date()}];
}
someMethod(){
// get result of Type ActionInstruction from Dialog -> newInstruction
this.actionInstructions.push(newInstruction);
}
The content of the array gets displayed in the view like follows:
<div *ngFor="let actionInstruction of actionInstructions">
<div *ngIf="actionInstruction.post_date_start" (click)="openActionCreation(actionInstruction)" class="action-instruction">
<div class=" fl ml-50">
Am {{actionInstruction.post_date_start.toLocaleDateString()}}
</div>
</div>
</div>
At start the elements that are added in the constructor, are getting with the localized date without any problem. But after adding an element dynamically via the form, the following error gets thrown:
At first I thought it could be caused by calling it during runtime in the view, but the problem actually occures in another totally independent component, where I prepare the variable to be shown in the view, in the constructor with the same function.
constructor(){
if(this.user.birthday != null){
this.localizedBirthday = this.user.birthday.toLocaleDateString();
}
}
Here it seems allmost patternless when the error occures and when not.
The only thing that both components have in common, that they use some *ngIf pattern in the view component, where the date get´s displayed.
Not a direct solution but you could use the datepipe in your components.
https://angular.io/api/common/DatePipe
it would look something like this:
<div class=" fl ml-50">
Am {{actionInstruction.post_date_start | date:'medium' }}
</div>

Open an angular template reference in a new browser window

So here is the explanation of the problem I am facing. It might look very similar other already asked questions, but none of them answered my problem.
I want to open an angular template reference in a new browser window (with all the styles) and use that window to print the contents using system print dialog.
By template reference I mean a whole component or may be just a fraction of template of given component.
Please note that I do not want to do it by below methods:
Opening a new route in a new browser window. (Why? this will cause all other things like common toolbar or help open up with the component. Also a new route will be required which is undesired.)
Opening the content in a closable modal.
Ok, This is how I did it using ComponentFactoryResolver and Injector. Just inject these two dependencies in your component which wants to open other component (ReportComponent in this case) in new browser window.
The constructor looks something like this in snippet below.
constructor(
private resolver: ComponentFactoryResolver,
private injector: Injector
) {}
.
.
.
This method opens up the component in new browser window.
public openPrintableReport(): void {
// resolve and instantiate the component which you want to open in new window.
const componentFactory = this.resolver.resolveComponentFactory(ReportComponent);
let component = componentFactory.create(this.injector);
component.changeDetectorRef.detectChanges();
// define external window
this.externalWindow = window.open('', '', 'width=1000,height=1000,left=150,top=200');
// copy all the styles to external window.
document.querySelectorAll('style').forEach(htmlElement => {
this.externalWindow.document.head.appendChild(htmlElement.cloneNode(true));
});
this.externalWindow.document.title = 'Printer Friendly Report';
// attach the component to external window
this.externalWindow.document.body.appendChild(component.location.nativeElement);
}

issue to make a component add a new component

I am building a restaurant review website with react js,html and css. I need to make a child component RestaurantInput update a sibling component Restaurant list.
I created handlers which pass informations to App component(the parent) by a callback and when there is an input change in the RestaurantInput it get updated by the handlers. The App component pass then the information to RestaurantList component by props which will render the new restaurant on the UI.
Unfortunatly there is no rendering of the new restaurant . I do not know where i got it wrong. Is there anyone who can help?
I have tried to console log the Restaurants imported from a Json at my local pc. But it look like it was not updated either.
I went to the React js documentation but did not get any clear answer either.
Many solution are for when there is a proper JSON file from the back end and I could not figure out how to apply them in my current situation.
RestauranInput.jsx:
handlechange(e){
const name=e.target.name;
const value=e.target.value;
this.setState((prevState)=>{
prevState.restaurant[name]=value;
return{restaurant:prevState.restaurant};
});
}
handleSave=(e)=>{
this.props.onSave(this.state.restaurant);
this.setState({
restaurant:Object.assign({},Init_value),
error:{}});
e.preventDefault();
}
App.js:
class App extends React.Component {
constructor(props){
super(props);
this.handlerestaurantclick=this.handlerestaurantclick.bind(this);
this.saveRestaurant=this.saveRestaurant.bind(this);
this.state={restaurants:Restaurantlist,showcomponent:false,
restaurantClicked:-1,newrestaurant:{}}
}
saveRestaurant(restaurant){
if(!restaurant.key){
restaurant.key= Object.keys(this.state.restaurants).length;}
this.setState((prevState)=>
{
let restaurants=prevState.restaurants;
restaurants[restaurant.key]=restaurant;
return{restaurants};
});
}
RestaurantList.jsx:
let list=[];
restaurantArray.forEach((item,index)=>{
list.push(<Restaurant key={index} name=
{item.restaurantName}
adress={item.address} ratings={item.ratings} onClick=
{()=>this.handleclick(index)}> </Restaurant>)})
return(<div className="restaurant-list">
<Filter getmin_filter={this.state.handle_min} get_max=
{this.state.handle_max}/>
{list}
</div>);
}
props are not states if they change  on the parent the child components are not rerender so you have to use "componentDidUpdate" check the link below
Re-render React component when prop changes
any communication that is not parent to child, you can either use events or states manager like redux

Is it possible to create a Polymer element without Html?

My final objective is don't have to write HTML like this:
<div id='counter'>
{{counter}}
</div>
<div>
<button
id="startButton"
on-click="{{start}}">
Start
</button>
<button
id="stopButton"
on-click="{{stop}}">
Stop
</button>
<button
id="resetButton"
on-click="{{reset}}">
Reset
</button>
</div>
I would like to know if it is possible to create a Polymer-element without using HTML. For example I tried this:
#CustomTag('tute-stopwatch')
class TuteStopWatch extends PolymerElement {
ButtonElement startButton,
stopButton,
resetButton;
#observable String counter = '00:00';
TuteStopWatch.created() : super.created() {
createShadowRoot()..children = [
new DivElement()..text = '{{counter}}',
new DivElement()..children = [
startButton = new ButtonElement()..text = 'Start'
..onClick.listen(start),
stopButton = new ButtonElement()..text = 'Stop'
..onClick.listen(stop),
resetButton = new ButtonElement()..text = 'Reset'
..onClick.listen(reset)
]
];
}
}
Previous code creates HTML and shadow root correctly, but it doesn't create the binding between the #observable counter and the text of the DivElement.
I know that this is caused because I am trying to create the shadow root after the element has been instantiated/created. So that I should create the template of the element in other place before the template has been bound with its observable.
You can write a manual data binding like this:
changes.listen((changes) {
for (var change in changes) {
if (change.name == #counter) {
myDivElement.text = change.newValue;
}
}
});
changes is a property of the Observable class, which PolymerElement mixes in. (This is difficult to see in the API reference, as it currently doesn't show a class' mixins or the mixed in properties and methods.)
Polymer seems to be mostly about enabling declarative html based bindings. It may be worth exploring using custom elements and shadow dom directly, as you're not really using polymer for anything in this example. To do this you need to change the class definition to:
class TuteStopWatch extends HtmlElement with Observable {
...
}
And register your element with document.register(). You also need to include the polymer.js polyfill for custom elements.