Apostrophe cms - inline editing of rich text in custom widgets? - node.js

I can't make inline editing of rich text save back to the db in some cases.
Please bear with me, there will be some code pasted here, as it is the only way I can describe what I'm doing.
I have two kinds of custom widgets in my project - the ones where there is only one instance of the widget, typically defined like this in the lib\modules directory:
article-widgets
- views
- - widget.html
- index.js
And then the kind of widgets that are repeated, and can be used in several places around the site, typically defined like this:
employees
- index.js
employees-widgets
- views
- - widget.html
This one I can make work:
In the first kind I define a rich text are in article-widgets\index.js
{
name: 'ingress',
label: 'Ingress',
type: 'area',
required: true,
options: {
widgets: {
'apostrophe-rich-text': {
toolbar: ['Bold', 'Italic', 'Link', 'Unlink' ]
}
}
}
}
And then in article-widgets\views\widget.html
{{ apos.singleton(data.widget, 'ingress', 'apostrophe-rich-text',{
toolbar: [ 'Bold', 'Italic', 'Link', 'Unlink' ]
}) }}
This one is not working for me:
In the second type - I can edit inline, but changes are not saved.
In employees\index.js
{
name: 'body',
label: 'Beskrivelse',
type: 'area',
options: {
widgets: {
'apostrophe-rich-text': {
toolbar: ['Bold', 'Italic', 'Link', 'Unlink']
}
}
}
}
And then in employees-widgets\views\widget.html
{% for piece in data.widget._pieces %}
<div>
{{
apos.singleton(piece, 'body', 'apostrophe-rich-text', {
toolbar: [ 'Bold', 'Italic', 'Link', 'Unlink' ]
})
}}
</div>
{% endfor %}
I can't understand why the later is not working. Am I doing something wrong - or is inline editing of repeated items not possible?

One of Apostrophe's subtleties is that any property beginning with an underscore (_) (except _id) is not actually part of the parent object and changes don't go back to the database
These are properties attached for convenience by Apostrophe somewhere in the retrieval process.
I don't thinkg modifying the piece from the widget is something baked in by default (typically piece-widgets are single-serving views for content) but it is possible to set up custom backend functionality.
Not a total 1:1 of your issue but if you look at the comments and comments-widgets module in this (rough) example, you can see we're modifying the piece's content via the widget.
https://github.com/stuartromanek/apostrophe-comment-system/tree/master/lib/modules

Related

What is the correct way to build multi-color template using Tailwind CSS?

Creating a custom color scheme for a template is very easy in Tailwind CSS. You just modify tailwind.config.js, add your custom color palate, and use it just like Tailwind's ordinary classes. For example bg-brand-500:
theme: {
extend: {
colors: {
brand: {
'50': '#B0ECEC',
'100': '#A0E8E8',
'200': '#7FE1E1',
'300': '#5ED9D9',
'400': '#3DD1D1',
'500': '#2CB9B9',
'600': '#218C8C',
'700': '#165E5E',
'800': '#0C3131',
'900': '#010404'
},
}
}
}
Now I'm stuck at a way to make a multi-color template.
I'm sure you have all seen templates all over the web where you can choose red or blue for example and the entire template's color scheme changes.
How do you do that in Tailwind?
Update:
In other CSS schools, like SASS, you simply create another color variables file and dynamically load a different file using the regular <link href='/path/to/red/variables.css' />.
You can use CSS variables for that.
In your tailwind config, you create the brand colors as you did, but instead of hex color codes, you use for example 50: 'var(--brand-50)'. Then in your index.css you can add these variables to the base layer, like:
#layer base {
:root {
--brand-50: #B0ECEC;
}
.theme-red {
--brand-50: #BB0000;
}
}
Now, if you add the class .theme-red to your body, text-brand-50 will be red.
In this video of Tailwind labs it is fully explained. There is also explained how to deal with opacity, although since tailwind 3.1 there is an easier way of doing that.
Hope this helps.
You might use the tw-colors plugin.
Create your themes
const { createThemes } = require('tw-colors');
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
plugins: [
createThemes({
light: {
brand: {
'50': '#B0ECEC',
'100': '#A0E8E8',
'200': '#7FE1E1',
'300': '#5ED9D9',
'400': '#3DD1D1',
'500': '#2CB9B9',
'600': '#218C8C',
'700': '#165E5E',
'800': '#0C3131',
'900': '#010404'
},
},
dark: {
brand: {
'50': '#321321',
'100': '#654654',
'200': '#987987',
'300': '#786541',
'400': '#aeeeee',
'500': '#786541',
'600': '#987987',
'700': '#165E5E',
'800': '#654654',
'900': '#321321'
},
}
})
],
};
Use your themes
<html class='theme-light'>
<div class="bg-brand-500">...</div>
</html>
You can then switch themes dynamically as you like, with a switch for example

Navigation icons load in layout.html, but not in fragment for apostrophe-headless api

I've created a fragment for apostrophe-headless, which loads my main navigation menu.
'apostrophe-pages': {
restApi: true,
apiTemplates: [ 'leftMenu']
},
This fragment has two functions:
in views/layout.html I {% include "apostrophe-pages:api/leftMenu.html" %} it
and I also pass it onto our angular app through /api/v1/apostrophe-pages/?render=leftMenu.html
in that fragment, I build my menu, including a few navicons.
{%- for tab in homeData._children -%}
{% set imageURL = apos.attachments.url(apos.images.first(tab.navicon), { size: 'one-third' }) or null %}
{{ apos.log(tab.navicon) }}
<li><img src="{{ imageURL }}"></li>
{% endfor %}
PROBLEM: These tab.navicons are not being loaded when this fragment is called through the API.
views/layout.html console.log of tab.navicon
_docId:"cjiqwpbjb00202zya1uf6wcm8"
_dotPath:"navicon"
_edit:true
items:Array(1) [Object]
type:"area"
__proto__:Object {constructor: , __defineGetter__: ,
__defineSetter__: , …}
/api/v1/apostrophe-pages/?render=leftMenu.html console.log of tab.navicon
items:Array(1) [Object]
type:"area"
__proto__:Object {constructor: , __defineGetter__: ,
__defineSetter__: , …}
Any thoughts?
Could it be a filters/depth issue?
Currently this is not possible with apostrophe-headless in a single call. Both the root GET route that fetches the entire tree and the per-page GET route currently have areas(false) hardcoded for performance when loading children.
This could be remedied by adding an initially empty method that is given a chance to modify that cursor object before the query takes place. Then project-level overrides would be possible. That would require a pull request to apostrophe-headless but it wouldn't be a very difficult one.
In the meantime, the workaround is to make additional apostrophe-headless calls for each of the child pages. The response for the page itself will always load areas (and thus join with images, populating the data you are looking for). For performance you could cache these responses in your client code.
I have opened a github issue on this.

How to differentiate between web elements that contain the same class

I want to make a list of elements that contain all the elements from these web elements:
<div class="league-item-block" id="LeagueId_(here there is a different id for every web element)" data-bind="css: { 'Hidden': $data.IsHidden() }, attr: { id: league.getDomId() }"></div>
and I use the syntax:
list = driver.find_elements_by_class_name("league-item-block")
The problem is that also have the web elements:
<div class="league-item-block Hidden" id="LeagueId_(here there is a different id for every web element)" data-bind="css: { 'Hidden': $data.IsHidden() }, attr: { id: league.getDomId() }"></div>
The list that is created contains these elements, and I do not know how to avoid them.
This can solve your problem -- CSS Selector:
.league-item-block:not(.Hidden)
Code:
list = driver.find_elements_by_css_selector(".league-item-block:not(.Hidden)")

Using Bootstrap Tabs with Angular Formly

Is there a way to include a "tab" structure in the formly json structure to build bootstrap-ui tabs easily? For example something kind of like this:
{
Tabs: [
{
heading: 'Tab 1',
fields: [
....
]
},
{
heading: 'Tab 2',
fields: [
....
]
},
]
}
Is there an easy easy to build this type of structure? Or should I do an ng-repeat on the tabs array with formly inside the ng-repeat? Or are their performance concerns with that or any other concerns?
I would recommend not putting the tabs in formly. Rather, put formly in tabs. If you're using angular-ui-bootstrap, it'd be structured something like this:
<tabset>
<tab>
<formly-form></formly-form>
</tab>
<tab>
<formly-form></formly-form>
</tab>
</tabset>
They can even share the same model. And you could do an ng-repeat on the tabs with an array of arrays of formly field configurations. If you like, you can request an actual example of this on the website repo

Trying to create onclick event in node js grid using KOGrid

I would like to have a nice interactive grid view in an HTML page. I am using nodejs, express, twitterbootstrap, knockoutjs, for my technology stack. I am trying to use KOGrid to display various data points with some nice built in column sorting and other grid functionality.
My issue is trying to fire an event when a button is clicked in a row. And pass to that event, some of the various data fields from that specific row. So in KOGrid specifics, I am using cellTemplates and I need to call some function in the onclick event, but pass that function some KOGRID data bounded values. So, in my input element I would have
data-bind="onclick: [Name of my function]( [name of some data bounded variable], [name of some other data bounded variable])
Can someone show me how to do this?
Here is a sample of my code...the input/onclick in the CBTemplate is where I am having issues.
CBTEMPLATE:
<script type="text/html" id="actionTemplate">
<div data-bind="kgCell: $cell">
<input type="checkbox" value="1" class="checkbox" checked="checked" data-bind="onclick: 'MyOnClickFunction( siteId(), status() )'"/>
</div>
</script>
DIV TAG:
<div data-bind="koGrid: { data: offer.siteCounts,
columnDefs: [ { field: 'templateField0', displayName: 'Site', cellTemplate: 'siteTemplate', width: 150},
{ field: 'status', displayName: 'Current Status', cellClass: 'site', cellTemplate: 'statusTemplate', width: 115},
{ field: 'details', displayName: 'Details', width: 175},
{ field: 'actionField0', displayName: 'Action', cellTemplate: 'cbTemplate', width: 200}],
autogenerateColumns: false,
displaySelectionCheckbox: false,
isMultiSelect: false }">
</div>
The click event binding should look like this with KO:
data-bind="click: function(data,event) { MyOnClickFunction(siteId(), status()) }"
Here is a working JSFiddle where you can play with it.
Some sidenotes:
In my sample I have used the $root binding context property to access the sample function: $root.offer.MyOnClickFunction. You will need to adjust this depending on which level you have defined the MyOnClickFunction on your viewmodels.
You need to return true from the click binding handler if you do want to let the default click action proceed (e.g checking the checkbox etc.)

Resources