Coveo card view on mobile with default template - coveo

I've been asked to disable the card view for a search result page. I know nothing about Coveo, but i have determined that for mobile the list view is prefered over card view. This is being implemented in a AEM 6.3 template.
In CoveoJsSearch.js, starting at line 17728 I have the following:
if (merged.responsiveComponents.isSmallScreenWidth()) {
templates = _.filter(templates, function (tmpl) { return tmpl.layout == 'card'; });
merged.currentLayout = 'list';
this.layout = 'list';
}
I changed these to list from card and it does what i want, but this looks JS file looks like it's 'compiled' from the front end framework. So what is the best way to ensure mobile users see a list view rather than a card view for search results.

Related

CKEditor 5: How can I load to/save from model instead of view?

My application uses CKEditor 5 to allow users to edit rich text data. These texts support some application-specific custom elements (Web Components), and I want to extend CKEditor with custom plugins that support inserting such custom elements. I seem to be almost there, but I'm having some difficulties getting these custom elements into and out of the CKEditor instance properly.
Current plugin implementations
I mainly followed the Implementing an inline widget tutorial from the CKEditor 5 documentation. As an example, I would like to support a custom element like <product-info product-id="123"></product-info>, which in CKEditor should be rendered as a simple <span> with a specific class for some styling.
In my editing plugin, I first define the extension to the schema:
const schema = this.editor.model.schema;
schema.register('product-info', {
allowWhere: '$text',
isInline: true,
isObject: true,
allowAttributes: [ 'product-id' ]
});
I then define the upcast and downcast converters, closely sticking to the tutorial code:
const conversion = this.editor.conversion;
conversion.for('upcast').elementToElement({
view: {
name: 'span',
classes: [ 'product-info' ]
},
model: (viewElement, { writer: modelWriter }) => {
const id = viewElement.getChild(0).data.slice(1, -1);
return modelWriter.createElement('product-info', { 'product-id': id });
}
});
conversion.for('editingDowncast').elementToElement({
model: 'product-info',
view: (modelItem, { writer: viewWriter }) => {
const widgetElement = createProductInfoView(modelItem, viewWriter);
return toWidget(widgetElement, viewWriter);
}
});
conversion.for('dataDowncast').elementToElement({
model: 'product-info',
view: (modelItem, { writer: viewWriter }) => createProductInfoView(modelItem, viewWriter)
});
function createProductInfoView(modelItem, viewWriter) {
const id = modelItem.getAttribute('product-id');
const productInfoView = viewWriter.createContainerElement(
'span',
{ class: 'product-info' },
{ isAllowedInsideAttributeElement: true }
);
viewWriter.insert(
viewWriter.createPositionAt(productInfoView, 0),
viewWriter.createText(id)
);
return productInfoView;
}
Expected behavior
The idea behind all this is that I need to support the custom <product-info> elements stored in user data in the backend. CKEditor, which is used by users to edit that data, should load these custom elements and transform them into a styled <span> for display purposes while editing. These should be treated as inline widgets since they should only be able to be inserted, moved, copied, pasted, deleted as a whole unit. A CKEditor plugin should allow the user to create new such elements to be inserted into the text, which will then also be <span>s in the editing view, but <product-info>s in the model, which should also be written back to the backend database.
In other words, I expected this to ensure a direct mapping between element <product-info product-id="123"></product-info> in the model, and <span class="product-info">123</span> in the view, to support inserting and moving of <product-info> elements by the user.
Actual result
In short, I seem to be unable to get CKEditor to load data containing <product-info> elements, and unable to retrieve the model representation of these custom elements for backend storage. All operations to insert data to CKEditor from source, or to retrieve CKEditor data for sending to the backend, seem to operate on the view.
For example, if I preload CKEditor contents either by setting the inner content of the element that is replaced with the editor instance, or inserting it like this:
const viewFragment = editor.data.processor.toView(someHtml);
const modelFragment = editor.data.toModel(viewFragment);
editor.model.insertContent(modelFragment);
I see the following behavior (verified using CKEditor Inspector):
When inserting the custom element, i.e. <product-info product-id="123"></product-info>, the element is stripped. It's not present in either the model nor the view.
When inserting the view representation, i.e. <span class="product-info">123</span> I get the representation that I want, i.e. that same markup in CKEditor's view, and the <product-info product-id="123"></product-info> tag in the model.
This is exactly the opposite of what I want! In my backend, I don't want to store the view representation that I created for editing purposes, I want to store the actual custom element. Additionally:
My UI plugin to insert new product info elements, uses a command that does the following:
execute({ value }) {
this.editor.model.change( writer => {
const productInfo = writer.createElement('product-info', {
'product-id': value
});
this.editor.model.insertContent(productInfo);
writer.setSelection(productInfo, 'on');
});
}
which also works as I want it to, i.e. it generates the product-info tag for the model and the span for the view. But, of course, when loading an entire source text when initialising the editor with data from the backend, I can't use this createElement method.
Conversely, in order to retrieve the data from CKEditor for saving, my application uses this.editor.getData(). There, these proper pairs of <product-info> model elements and <span> view elements get read out in their view representation, instead of their model representation – not what I want for storing this data back!
The question
My question is: what do I need to change to be able to load the data into CKEditor, and get it back out of the CKEditor, using the custom element, rather than the transformed element I want to show only for editing purposes? Put differently: how can I make sure the content I insert into CKEditor is treated as the model representation, and how do I read out the model representation from my application?
I'm confused about this because if the model representation is something that is only supposed to be used internally by CKEditor, and not being able to be set or retrieved from outside – then what is the purpose of defining the schema and these transformations in the first place? It will only ever be visible to CKEditor, or someone loading up the CKEditor Inspector, but of no use to the application actually integrating the editor.
Sidenote: an alternative approach
For a different approach, I tried to forgo the transformation to <span>s entirely, and just use the custom element <product-info>, unchanged, in both the model and the view, by using the General HTML Support functionality. This worked fine, since this time no transformation was needed, all I had to do was to set the schema in order for CKEditor to accept and pass through the custom elements.
The reason I can't go with this approach is that in my application, these custom components are handled using Angular Elements, so they will actually be Angular components. The DOM manipulation seems to interfere with CKEditor, the elements are no longer treated as widgets, and there are all manner of bugs and side effects that come with it. Elements show up fine in CKEditor at first, but things start falling apart when trying to select or move them. Hence my realisation that I probably need to create a custom representation for them in the CKEditor view, so they're not handled by Angular and preventing these issues.

Dynamic Rendering of UI Elements in PCL Application?

I have a question,is it possible to render labels or buttons or any UI element on the form dynamicaly?
for Eg:
I am building a Mobile app which is replicate of my website, so the pages and fields will be same the UI will be different.
if on the website i add a new field in a form, so can i add that field directly to my Mobile Form without coding anything?
So Basically i want to store it in the Database, and when loading the page fetch the values and render the UI.
Is this Possible with Xamarin forms?
StackLayout sl = new StackLayout();
// controls is an IEnumerable of control types
foreach(var s in controls) {
switch (s.ControlType) {
case "Label":
sl.Add(new Label() { Text = s.Caption });
break;
case "Textbox":
sl.Add(new Entry());
break;
case "Button":
sl.Add(new Button() { Text = s.Caption });
break;
// add additional cases for whatever types of controls you want to support
}
}
Yes. This is possible. You can store Xamarin Forms XAML in the database, and render that XAML dynamically without writing code. The answer above will work, but it's not an elegant solution because you will need to write code.
If you clone this repo, there is a sample for rendering XAML dynamically:
https://github.com/MelbourneDeveloper/Adapt.Presentation.git
You can store the XAML in the database and use this method to load the UI.

LightSwitch Tabbed screen in Browse template

I have a screen where we have 4 tabs, each tab should be displayed as per the login priority.
Ex:Department,Role,Employee,Screen are the tabs.
Each tab is having buttons to add,edit,remove the data.
by default when i log with any user its going to the first tab, but not all the users are having the first tab as their requirement.
how can i resolve this to do it dynamically in html client application
As covered towards the end of the following LightSwitch Team blog post, you can programmatically change the tab by using the screen.showTab method:
Creating a wizard-like experience for HTML client (Andy Kung)
However, in order to use this showTab API command when your screen is loading, its use needs to be delayed until the screen has fully displayed. This can be achieved in your screen's created method by using a combination of the jQuery mobile pagechange event (as the LightSwitch HTML Client uses jQuery mobile) and a setTimeout with a zero timeout (to delay the showTab until the loading screen is rendered).
The following shows a brief example of how you can use this approach to dynamically set the initial screen tab:
myapp.BrowseScreen.created = function (screen) {
var initialTabName = localStorage.getItem("Rolename") + "Tab";
$(window).one("pagechange", function (e, data) {
setTimeout(function () {
screen.showTab(initialTabName);
});
});
};
Based on your earlier post it appears that you're using LocalStorage to track your logged in user and their role.
On this basis, the above example assumes that the user's role will be the factor dictating the tab they are shown when the screen loads (the screen is named BrowseScreen in the above example).
It also assumes that your tabs are named after each employee role (suffixed with the text 'Tab') e.g. a user who is assigned the role 'DepartmentManager' would be directed to a tab called 'DepartmentManagerTab'.
Whilst slightly more involved, if you'd prefer to avoid the pagechange and setTimeout it's possible to customise the LightSwitch library to introduce a new navigationComplete screen event. This new event is ideal for executing any operations dependent upon the screen having fully rendered (such as navigating to a different tab using the showTab function).
If you'd like to introduce this additional event, you'll need to reference the un-minified version of the LightSwitch library by making the following change in your HTML client's default.htm file (to remove the .min from the end of the library script reference):
<!--<script type="text/javascript" src="Scripts/msls-?.?.?.min.js"></script>-->
<script type="text/javascript" src="Scripts/msls-?.?.?.js"></script>
The question marks in the line above will relate to the version of LightSwitch you're using.
You'll then need to locate the section of code in your Scripts/msls-?.?.?.js file that declares the completeNavigation function and change it as follows:
function completeNavigation(targetUnit) {
msls_notify(msls_shell_NavigationComplete, { navigationUnit: targetUnit });
var screen = targetUnit.screen;
var intialNavigation = !screen.activeTab;
var selectedTab = targetUnit.__pageName;
if (screen.activeTab !== selectedTab) {
callNavigationUnitScreenFunction(targetUnit, "navigationComplete", [intialNavigation, selectedTab]);
screen.activeTab = selectedTab; // Set at the end of the process to allow the previous selection to be referenced (activeTab)
}
}
function callNavigationUnitScreenFunction(navigationUnit, functionName, additionalParameters) {
var screenObject = navigationUnit.screen;
var constructorName = "constructor";
var _ScreenType = screenObject[constructorName];
if (!!_ScreenType) {
var fn = _ScreenType[functionName];
if (!!fn) {
return fn.apply(null, [screenObject, navigationUnit].concat(additionalParameters));
}
}
}
You can then use this new event in your screens as follows:
myapp.BrowseScreen.navigationComplete = function (screen, navigationUnit, intialNavigation, selectedTab) {
if (intialNavigation) {
var initialTabName = localStorage.getItem("Rolename") + "Tab";
screen.showTab(initialTabName);
}
};
This event fires whenever a navigation event completes (including a change of tab) with the initialNavigation parameter being set to true upon the initial load of the screen and the selectedTab parameter reflecting the selected tab.
Although modification to the LightSwitch library aren't uncommon with some of the more seasoned LightSwitch developers, if you decide to go down this path you'll need to thoroughly test the change for any adverse side effects. Also, if you upgrade your version of LightSwitch, you'll need to repeat the library modification in the new version.

Adding URL adresses to views in SAPUI5

I have SAPUI5 application where I am using Shell. I have different views in my Shell and I am able to navigate from one view to another. The problem is that I don't know how can I create or change URL adress to each view. I need to set URL adresses to be able to use "back" button in a browser. I want to add unique URL adress to each view inside my Shell to use "back" and "forward" buttons.
Thanks!
Take a look at this simple sap.ui.core.routing.Router example it shows how to easily set up and use routes and a hash changer with an app Shell container.
sap.ui.core.routing is a javascript routes utilty, it handles URL changes and dispatches control based on pattern logic to views and event handlers, part of routing is HashChanger and History functionality.
if you are using the desktop shell this routing example with sap.ui.ux3.Shell
A lot of examples use jQuery.sap.history.js IMO it doesn't properly support bookmarks and has limited features which means you have to use it with other techniques
Unfortunately, History API presents in modern browsers only. For IE6+ I recommend you to use "hashchange" event of window:
function router(){
var sViewName = window.location.hash.slice(1),
oView = sap.ui.getCore().byId(sViewName) || new sap.ui.view.XMLView({id : sViewName, viewName : sViewName});
sap.ui.getCore().byId("uxShell").setContent(oView);
return false;
}
if("onhashchange" in window){
if(window.attachEvent){ //IE8
attachEvent('onhashchange', router);
} else if(window.addEventListener){ //IE9+, Chrome, Opera, Firefox
addEventListener('hashchange', router);
}
} else { //IE6-7
var oldHref = window.location.hash;
setInterval(function(){
var newHref = window.location.hash;
if(oldHref !== newHref){
oldHref = newHref;
router();
}
}, 100);
}
But to make it work you need to set href property to navigation items like this: #catalog.viewName

Preview Of Gallery in Django Admin

I want to display the selected gallery in my admin. I'm not very capable of writing custom fields and couldn't find any well documented guidelines about it.
As for my question, I've written basic classes such as:
class GalleryViewWidget(forms.TextInput):
def render(self,name,value,attrs):
rendered = super(GalleryViewWidget, self).render(name, value, attrs)
return rendered + mark_safe(....)
class ProductModelForm(forms.ModelForm):
information = forms.CharField(widget=forms.Textarea)
gallery = GalleryViewWidget
class Media:
css = {
'all': (settings.MEDIA_URL + 'css/preview.css',)
}
js=(
"http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js",
settings.MEDIA_URL + 'js/preview.js',
)
class Meta:
model = Product
In my preview.js file, I want to send an ajax request, the problem is I don't know where to handle this ajax call. In my ProductModelForm ?
I'd really appreciate that if anyone gives me any knowledge about how to handle this ajax thing or another way to display selected gallery in my admin ?
Here I saw a bit outdated tutorial about it...
It creates your own thumbnails. You need to use "sorl-thumbnail" now-days for thumbnails generation and storing it's a bit easier and more right way IMHO...
Nevertheless it's a tutorial of how to build a photo previews in admin. You could use it or enhance it with AJAX calls. But IMHO again it's not necessary...
P.S. It's better to download full sources of this app from the start.
so... article:
Django Tutorial: Photo Organizer and Sharing App Part I. Enhancing Admin.
Your widget (with extra feature of handling postfix in file name) might look like this:
class ImageThumbnailWidget(forms.FileInput):
def __init__(self, postfix=None, attrs={}):
self.postfix = postfix
super(ImageThumbnailWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
output = []
if value and hasattr(value, "url"):
img_path = value.url.split('/')[:-1]
img_path = "/".join(img_path)
img_name = value.url.split('/')[-1]
if self.postfix:
name_parts = img_name.split(".")
ext = name_parts.pop()
img_name_start = "_".join(name_parts)
img_name = "%s%s.%s" % (img_name_start, self.postfix, ext)
output.append('%s<br/><img src="%s/%s" /> <br />%s ' %
(_('Currently:'), img_path, img_name, _('Change:')))
output.append(super(ImageThumbnailWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
Hope it helps. If it doesn't fit your needs, write some more details and I'll try to figure out something (I'd like to know where exactly do you want to show the preview of gallery - is it "changelist" or "change_view" of product, where you have inline formset with images).
I'll answer the Where do I put my Admin/ModelForm Ajax views? part of your question, for the gallery part maybe have a look at photologue.
As for creating views which are called from the admin forms, I found creating simple custom views the easiest. In your Javascript code you just call {% url my_ajax_view %} with data specific to your app.
For example (a modified version of ajaxy foreignkey search):
class ForeignKeySearchInput(forms.HiddenInput):
"""
A Widget for displaying ForeignKeys in an autocomplete search input
instead in a ``select`` box.
"""
[ ... stuff removed ... ]
def render(self, name, value, attrs=None):
[ ... snip ... ]
context = Context({
'search_url': reverse('tools_autocomplete_search'),
'model_name': self.rel.to._meta.module_name,
'app_label': self.rel.to._meta.app_label,
[ ... snip ... ]
})
template = loader.get_template('admin/widgets/foreignkeysearchinput.html')
return rendered + mark_safe(template.render(context))
The key here is to hand the required data to the widget template, which then uses this data to call the ajax callback correctly.
The actual view then is as simple (or complicated) as your problem.
def ajax_search(request):
"""
Searches in the fields of the given related model and returns the
result as a simple string to be used by the jQuery Autocomplete plugin
"""
query = request.GET.get('q', None)
app_label = request.GET.get('app_label', None)
model_name = request.GET.get('model_name', None)
search_fields = request.GET.get('search_fields', None)
[ ... snip ... ]
return HttpResponse(simplejson.dumps(data, indent=2))
Alternatively you can embedd ajax view into a ModelAdmin subclass (or a Mixin), but if you don't want to muck about with the internals of django.contrib.admin routing the above is way easier.

Resources