In AEM6, How do I hide a specific component field based on pages for certain country only? - content-management-system

In AEM6, How do I hide a specific component field based on pages for certain country only ?

You can write custom dialog/widget plugin to do that. This is how you attach plugin to your widget:
<title jcr:primaryType="cq:Widget"
fieldLabel="Field to hide"
plugins="hideFieldPlugin"
name="./fieldToHide"
xtype="textfield" />
Next, we need to write plugin and register it:
(function ($) {
var plugin = CQ.Ext.extend(CQ.Ext.emptyFn, {
init: function (fieldToHide) {
var url = CQ.HTTP.getPath();
if (this.shouldBeHidden(url)) {
fieldToHide.hide().disable();
}
},
shouldBeHidden: function (url) {
// some logic
return true;
}
});
CQ.Ext.ComponentMgr.registerPlugin("hideFieldPlugin", plugin);
}($CQ));
JavaScript file needs to be included in Classic UI edit mode. Best way to do that is to use your own custom clientlib or use already existing category, cq.wcm.edit.
If you have more complex logic which goes across multiple widgets, you can attach plugin on dialog level and navigate to the widget objects using dialog.find method.

Related

params for switch to favorites pane (Home Feature not loading favorites)

i have this requirement on IBM Content Navigator about a personalized (feature) homepage with various buttons used to switch between feature; i've made all works except for the one linked to the Home feature (favorites)
i've already tried to call the feature with thoose params:
params.repositoryId="FNOSARCHIVIO";
params.application="navigator";
params.desktop="OneFile";
params.userid="sys.filenetsvil";
but with no success, the feature is switched (after the button press it switch to the home feature) but it does not load the favorites of the user
here is my switch-feature method (taken for the ibm icn redbook + some modification)
switchFeature: function (featureIdToSwitch) {
//get layout from destop
var layout = ecm.model.desktop.getLayout();
// get the corresponding button of the LaunchBar Container
var feaButt = layout.launchBarContainer.getFeatureButtonByID(featureIdToSwitch);
var params = {};
// params.repositoryId="FNOSARCHIVIO";
// params.application="navigator";
// params.desktop="OneFile";
// params.userid="sys.filenetsvil";
// switching to the target feature
// feaButt.child.loadContent;
layout.launchBarContainer.selectContentPane(feaButt, featureIdToSwitch, params);
}
on the frontend i have 4 simple dojo buttons with onClick action, nothing special.
i use this feature id:
switchToHome: function () {
this.switchFeature('favorites');
},
this is what i mean when i say "it switch the feature but do not load the favorites:"
Home feature called from my button:
https://ibb.co/GMW7L2x
Home feature called from the standard toolbar:
https://ibb.co/BBgr36L
looks like it is loading the feature but it is not calling the listFavorites()
i cannot find any help on IBM docs or forum, any help here ? thanks!
At least i managed to do it, i post it here, hope helps someone:
1- override the default favorite feature (the java class), using the same js plugin, overriding this:
#Override
public String getContentClass() {
return "ecm.widget.layout.HomePane";
}
and set it to preLoaded:
#Override
public boolean isPreLoad() {
return true;
}
then, on the frontend, retrive the js feature, and load the content:
var targetFeature = layout.launchBarContainer.getContentPaneByID(featureIdToSwitch);
targetFeature.loadContent()
you can call the loadContent() only if the feature has been preLoaded or alredy invoke at leat once

How to seperate Vue logic in a laravel app based on layout and page templates

I have a laravel app and a Vue instance attached to the body (or a div, just inside the body).
const app = new Vue({
el: '#app'
});
I think it makes sense to use the Vue instance for stuff relating to the layout (eg header, nav, footer logic).
Now I have a form that is visible on a specific route (e.g. example.com/thing/create). I want to add some logic to it, e.g. hiding a field based on selected option in the form. It is logic meant for just this form (not to be reused). I prefer not to put all the logic inline with the form but put it in the app.js. I could put it in the Vue instance bound to the body but that sounds odd as it only applies to the form that is much deeper into the dom.
I want to leave the markup of the form in the blade template (that inherits the layout).
I tried creating a component but am not sure how to bind this inside the main Vue instance. What is the best way to handle things for this form, put it in the app.js and have it somewhat structured, putting the variables somewhat into scope. Or is it really necessary to remove the main Vue instance bound to the full layout code?
What I tried was something like this, but it does not work (attaching it to the <form id="object-form"> seems to fail:
var ObjectForm = {
template: function() { return '#object-form'},
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
}
};
Things do work if I remove the #app Vue instance or when I put everything directly in the app Vue instance. But that seems messy, if I have similar variables for another form they should be seperated somewhat.
I would appreciate some advice regarding the structure (differentiate page layout and page specific forms) and if possible some example to put the form logic inside the main app.js.
I hope this helps kind of break things down for you and helps you understand Vue templating.
It is best to take advantage of Vue's components. For you it would look something like this. Some of this code depends on your file structure, but you should be able to understand it.
In your app.js file (or your main js file)
Vue.component('myform',require('./components/MyForm.vue'));
const app = new Vue({
el: "#app"
});
Then create the MyForm.vue file
<template>
<form>
Put Your Form Markup Here
</form>
</template>
<script>
// Here is where you would handle the form scripts.
// Use methods, props, data to help set up your component
module.exports = {
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
},
methods: {
// add methods for dynamically changing form values
}
}
</script>
Then you will be able to just call in your blade file.
<myform></myform>
I found out how to do it. The trick was to use an inline template. Surround the form in the view with:
<object-form inline-template>
<form>...</form>
</object-form>
Where object-form is the name of the component. In the ObjectForm code above I remove the template, like this:
var ObjectForm = {
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
}
};
I attach the component within the root vue app like this:
const app = new Vue({
el: 'body',
components: {
'object-form': ObjectForm
}
});
This way I can use the form as it was generated from the controller and view and I can separate it from the root (attached to body) methods and properties.
To organize it even better I can probably store the ObjectForm in a seperate .vue file the way #Tayler Foster suggested.

How to start XML preprocessor without return a view component

I'm trying to create a launchpad for a set of applications that we use here. One of my problems is that I need to add different tiles in a tile container (slide,custom,standard, etc) and what I think that may be a solution is use XML templating to do that. What I want to achieve is something like that:
<TileContainer id="tileList"
allowAdd="true"
tileDelete="onDelete"
tiles="{path:'Atalhos>/' ,sorter:{path:'Atalhos>TileText',group:false}}">
<template:if test="{path:'Atalhos>TileCode', operator:'EQ',value1:'teste1'}">
<template:then>
<core:Fragment fragmentName="pelissari.soficom.launchpad.view.StandardTile" type="XML"/>
</template:then>
<template:else>
<core:Fragment fragmentName="pelissari.soficom.launchpad.view.StandardTile" type="XML"/>
</template:else>
</template:if>
</TileContainer>
but the problem is that I'm having this error when I try to do that.
UIComponent-dbg.js:52 Uncaught Error: failed to load
'http://schemas/sap/com/sapui5/extension/sap/ui/core/template/1/if.js'
from
resources/http://schemas/sap/com/sapui5/extension/sap/ui/core/template/1/if.js:
404 - Not found
I know that I need to start the preprocessor to use preprocessing instructions but all the examples that I found makes me more confuse that I was before.
My project is based on the sapui5 tutorial "WalkThrough where I have a component that starts an app view configured in the manifest and this view is navigate to the launchpad view by routing configuration again in the mainfest. all the examples create a view in the component CreateComponent function or in some controller function that loads other view. I just need to start the preprocessor for the list of tiles that I load from the entity set "/TileSet".
I found another way to do what I want. Now I'm using factory function to create the tiles as I need.
tileFactory: function(sId, oContext) {
var atalho = oContext.getProperty(oContext.sPath)
var oUIControl = null;
if (atalho.TileCode == 'teste2') {
oUIControl = new sap.m.StandardTile(sId, {
title: atalho.TileText
});
oUIControl.addStyleClass('tileSize3');
} else {
oUIControl = new sap.m.StandardTile(sId, {
title: atalho.TileText
});
oUIControl.addStyleClass('tileSize1');
}
oUIControl.attachPress(this.onPress, this);
oUIControl.addStyleClass('tile');
return oUIControl;
}
<Page id="tileGroup" showHeader="true"
content="{path:'Atalhos>/' ,sorter:{path:'Atalhos>TileOrder',group:false},factory:'.tileFactory'}">

Link to route without additional JS code

I'm a beginner to OpenUI5 and have written a menu like below:
view:
<NavigationList id="navigationList" width="12rem">
<NavigationListItem text="People" icon="sap-icon://card" select="goToRoute"></NavigationListItem>
</NavigationList>
controller:
goToRoute: function() {
this.getRouter().navTo("peoplelist");
}
That works, but it's bad, as I have a couple of menu items and JS events for each of them.
I wish I had something like this below, without any JS behind, but couldn't find anything in documentation and examples.
view:
<NavigationList id="navigationList" width="12rem">
<NavigationListItem text="People" icon="sap-icon://card" linkToRoute="peoplelist"></NavigationListItem>
</NavigationList>
Anyone knows?
You can build a generic event handler which derives the target route from the corresponding item. To achieve this you can make use of custom data which you can add to controls in a declarative way. For this add the following line to your opening View tag:
xmlns:custom="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1"
Adopt your navigation list:
<NavigationList id="navigationList" width="12rem">
<NavigationListItem text="People" icon="sap-icon://card" select="onItemSelect" custom:route="peopleList"/>
</NavigationList>
And your controller:
onItemSelect : function(event) {
var item = event.getSource();
var route = item.data("route");
this.getRouter().navTo(route);
}
Here you find further details.

Using search feature in Ionic framework

I am a UI person and very new to ionic framework.. I wanted to add search feature in my android app built using Ionic framework. After a research i found that I will need to use this plugin https://github.com/djett41/ionic-filter-bar. but there is no detail documentation available. Can anyone please guide how to use this plugin working. I have made all setup but stuck with actual code.
First of all you must install the plugin. You can use bower for that:
bower install ionic-filter-bar --save
and it will copy all the javascript and css needed in the lib folder inside www.
Then you must add the references to the css to your index.html:
<link href="lib/ionic-filter-bar/dist/ionic.filter.bar.css" rel="stylesheet">
same thing for the javascript:
<script src="lib/ionic-filter-bar/dist/ionic.filter.bar.js"></script>
You have to inject the module jett.ionic.filter.bar you your main module:
var app = angular.module('app', [
'ionic',
'jett.ionic.filter.bar'
]);
and you must reference the service $ionicFilterBar in your controller:
angular.module('app')
.controller('home', function($scope, $ionicFilterBar){
});
Now you can start using it.
In my sample I want to trigger the search-box when the user clicks/taps on a icon in the header. I would add this HTML to the view:
<ion-nav-buttons side="secondary">
<button class="button button-icon icon ion-ios-search-strong" ng-click="showFilterBar()">
</button>
</ion-nav-buttons>
The action trigger an event in my controller showFilterBar:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
items: $scope.places,
update: function (filteredItems, filterText) {
$scope.places = filteredItems;
}
});
};
which creates the $ionicFilterBar and shows it.
As you can see here I am using an array of objects $scope.places
$scope.places = [{name:'New York'}, {name: 'London'}, {name: 'Milan'}, {name:'Paris'}];
which I have linked to the items member of my $ionicFilterBar. The update method will give me in filteredItems the items (places) filtered.
You can play with this plunker.
Another option is to use the plugin to actually fetch some data remotely through $http.
If we want to achieve this we can use the update function again.
Now we don't need to bind the items to our array of objects cause we won't need the filtered elements.
We will use the filterText to perform some action:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
// items: $scope.places,
update: function (filteredItems, filterText) {
if (filterText) {
console.log(filterText);
$scope.fetchPlaces(filterText);
}
}
});
};
We will call another function which will, maybe, call $http and return some data which we can bind to our array of objects:
$scope.fetchPlaces = function(searchText)
{
$scope.places = <result of $http call>;
}
Another plunker here.
PS:
If you want to configure it using some sort of customization you must do it in your configuration using the provider $ionicFilterBarConfigProvider:
angular.module('app')
.config(function($ionicFilterBarConfigProvider){
$ionicFilterBarConfigProvider.clear('ion-ios-close-empty');
})
PPS:
In my plunker I've included the css and the script directly copying it from the source.
UPDATE:
Someone asked not to replace the list with the updated one.
My cheap and dirty solution is to check if the filterText contains some values. If it's empty (no searches) we go throught each element an set a property found = false otherwise we compare the places array we the filteredItems array.
Matching elements will be marked as found.
function allNotFound(filteredItems) {
angular.forEach($scope.places, function(item){
item.found = false;
});
}
function matchingItems(filteredItems) {
angular.forEach($scope.places, function(item){
var found = $filter('filter')(filteredItems, {name: item.name});
if (found && found.length > 0) {
console.log('found', item.name);
item.found = true;
} else {
item.found = false;
console.log('not found', item.name);
}
});
and now we can integrate the filter bar this way:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
items: $scope.places,
update: function (filteredItems, filterText) {
if (!filterText) {
allNotFound();
} else {
matchingItems(filteredItems);
}
}
});
};
We can use the found attribute of the object to change the style of the element.
As always, a Plunker to show how it works.
Ionic uses Angular, and Angular include an atributte filter very useful. Look this: https://docs.angularjs.org/api/ng/filter/filter and the example there. Regards