Grails 3.1 JSON-Views, rendering a Category Tree - angularjs

I'm using Graisl 3.1.1, rest-api profile.
I'm trying to build a Category Tree but I haven some problems rendering the categories in JSON-Views.
I'm using json templates, one for the parent and another for the child.
Basically I want to generate a json for angular something like this:
These is my code.
Any help?
//domain
class Category {
ObjectId id /* MongoDB */
static hasMany = [categories: Category]
String name
...
//controller
def directory(){
def categories = Category.findAllByCategoriesIsNotNull([sort: 'name', order: 'asc'])
respond categories
}
//directory.gson
import com.example.Category
model {
Iterable<Category> categoryList
}
json {
categories g.render(template: 'parent', collection: categoryList ?: [], var: 'category')
}
//_parent.gson
import com.example.Category
model {
Category category
}
json {
id category.id.toString()
name category.name
categories g.render(template: "category/child", collection: category.categories ?: [], var: 'child')
}
The problem is the categories line above, I'm not sure what is the problem or my mistake.
//_child.gson
import com.example.Category
model {
Category child
}
json {
name child.name
}

I'm reasonably confident you are encountering the same fresh bug (fixed in grails-views#1.0.4) that I encountered a couple weeks ago, #9720:
it would appear that the relative path isn't recognized for g.render and the fully qualified path is required even when the template is located in the same directory as the calling gson file.
Instead of:
categories g.render(template: "category/child", collection: category.categories ?: [], var: 'child')
prepend a slash to the template:
categories g.render(template: "/category/child", collection: category.categories ?: [], var: 'child')
You may also need to change:
categories g.render(template: 'parent', collection: categoryList ?: [], var: 'category')
to:
categories g.render(template: '/category/parent', collection: categoryList ?: [], var: 'category')

I usually use in controller
import grails.converters.JSON
and then
render result as JSON

Related

React Gatsby Graph QL - filter articles for sitemap

I have created a Gatsby blog using Flexiblocks. The issue is i want to create a sitemap specifically for Google News which will be different from main sitemap of the blog. For this sitemap i need to filter articles which were published within last two days.
I am using this plugin
allArticle(sort: {order: DESC, fields: date}, filter: {date: {WHAT_SHOULD_I_DO_HERE}}) {
edges {
node {
date
excerpt
title
link
slug
}
}
}
Any help will be appreciated, thanks.
UPDATE
{date: {gte: "2021-08-27T13:11:30.443Z"}} will filter the articles, but how can i get this date string dynamically?
Gatsby relies on momentjs under the hood when dealing with dates in GraphQL scope. You just need to add the variable returned from a function in the scope of the query like:
const getTwoDaysAgo=()=> moment().subtract(2, "days");
In your gatsby-config.js you just can call this function and:
To add a variable you can follow thee answers in: Variables in graphQL queries
Basically, following this syntax:
grapqhl(
`query getImages($fileName: String) {
landscape: file(relativePath: {eq: $fileName}) {
childImageSharp {
fluid(maxWidth: 1000) {
base64
tracedSVG
aspectRatio
src
srcSet
srcWebp
srcSetWebp
sizes
originalImg
originalName
}
}
}
}
`,
{fileName: "knight.jpg"}
)
You can add custom variables. In this case, note that the query is wrapped inside graphql() function. Applied to your case, you can try:
plugins: [
{
resolve: `gatsby-plugin-advanced-sitemap`,
options: {
query: graphql(
`
query allArticle(
sort: {
order: DESC, fields: date
},
filter: {date: {twoDaysAgo}}) {
edges {
node {
date
excerpt
title
link
slug
}
}
}`, {twoDaysAgo: getTwoDaysAgo()}
)
}
}
]
Note: be careful copy/pasting because there's a lot of brackets that I may miss.
I'm not sure if the plugin will support that notation but it should.
{date: {gte: "2021-08-27T13:11:30.443Z"}}
Given the edited question, adapt the flow and the filter to the gte parameter.

What's the best way to work with a relational firestore model?

I followed this video on the best practices for creating flat databases with firestore: Converting SQL structures to Firebase structures
I came up with something that looks like this:
const firestore = {
events: {
eventID: { // Doc
description: "Event Description", // Field
title: "Event Title", // Field
}
},
eventComments: { // Collection
eventID: { // Doc
comments: { // Field
commentID1: true, // Value
commentID2: true, // Value
commentID3: true, // Value
}
}
},
comments: { // Collection
commentID1: { // Doc
createdAt: "Timestamp", // Field
createdBy: "uid", // Field
content: "Comment Body" // Field
},
commentID2: {...},
commentID3: {...},
},
};
I'm not sure what the best way to get the related data is however
I'm using react and react-redux-firestore to access the data. My current setup for the app looks like this
<EventsDetailPage>
<Comments>
<Comment />
<Comment />
<Comment />
</Comments>
</EventsDetailPage>
I've come up with two potential methods...
Method 1
I have useFirestoreConnect in each component. The top level gets the event and passes the eventID to the comments component, the comments component uses the eventID to get the eventComments list which passes the individual commentID for each comment to the comment component, then finally the individual comment component uses the commentID to get the relevant comment data.
My issue with this: Wouldn't this mean that there is a listener for the event, comment list, and every individual comment? Is that frowned upon?
EX: This would be in the event, the comments, and comment component but each with respective values
useFirestoreConnect(() => [
{collection: 'events', doc: eventID},
]);
const event = useSelector(({firestore: {data}}) => data.events && data.events[eventID]);
Method 2
Let's say I have a list of events, I can do a query to get the lists
useFirestoreConnect(() => [{
collection: 'events',
orderBy: ["createdAt", "desc"],
limitTo: 10
}]);
const events = useSelector(({ firestore: { ordered } }) => ordered.events);
This is great because I believe it's one listener but if any of the data is changed in any of the events the listener will still respond to the changes.
My issue with this: I don't know how to do a where clause that would return all events for a given list of IDs.
So like say if I wanted to get a list of events with where: ['id', '==', ['eventID1', 'eventID2', 'eventID3']]
To retrieve up to 10 items by their ID, you can use an in query:
.where('id', 'in', ['eventID1', 'eventID2', 'eventID3'])
If you have more than 10 IDs, you'll have to run multiple of these queries.

How to get details from angularFire Database and set it into a template

I am trying to get a user details from a angularFire2 database. Therefore instead of using the FirebaseListObservable, I am using FirebaseObjectObservable since I am expecting just a user from the path.
This is the path and the colums through which I am deriving the user data
path: /projects/users/
columns: userID,username
data
userID:"001",
username:"Icomin"
And this the home.ts
import { Component } from '#angular/core';
import { NavController,AlertController,ActionSheetController } from 'ionic-angular';
import{AngularFireDatabase,FirebaseListObservable,FirebaseObjectObservable} from 'angularfire2/database';
import {Observable} from 'rxjs/Observable';
#Component({
selector: 'my-child-component',
template: `<h1>Welcome {{ (projects | async)}}</h1>` // The template
})
export class HomePage{
projects: FirebaseObjectObservable <any>;
constructor(af:AngularFireDatabase) {
this.projects = af.object(`/projects/users`);
}
I implement the template below, however the returnsWelcome [object object]
The template implementation
template: `<h1>Welcome {{ (projects | async)}}</h1>`
How do I get the user details from the above path and set it into the template
As you mention , you get Welcome [object object] from projects variable. It's mean that you receive a list from this call af.object('/projects/users') .
I dont really know what exactly you want to do with that list but you few options:
1 - show all users in list :
template: ' <h1 *ngFor="let proj of projects | async">{{ proj.username }}</h1>
this will display all user's names , one by one.
2 - show specific user:
(you need to choose the filter criteria: userID or username)
export class HomePage{
project: FirebaseObjectObservable <any>;
constructor(af:AngularFireDatabase) {
this.project = af.list(`/projects/users`,{
query: {
orderByChild: 'userID', //or orderByChild: 'username',
equalTo: <someUserID> // equalTo: <someUsername>
}
});
}
}
and then the template to display the current username:
template: <h1>{{ project.username }}</h1>
Hope it fits what you wanted.

Custom data unwrapping in ampersand.js model

I have a model - Configuration:
var Configuration = Model.extend({
props: {
name: 'string'
}
});
In the database, configuration model / table has 3 columns -> id, name and fields. The latter stores site config as a serialized array. When retrieving the entry from the database, I unserialize it and then pass it to the front end, so the front end receives this:
{
"id": 1,
"name": 'global',
"fields": {
"enabled": true,
"site_name": "Test"
}
};
What I want to do is to set whatever is inside fields object as properties on my model, or maybe session so that things get triggered throughout the site when they are updated. To visualize it, I want to achieve something like this:
var Configuration = Model.extend({
props: {
enabled: 'boolean',
site_name: 'string'
}
});
So basically, is there are a way to 'unwrap' stuff in fields object somehow?
The parse method is what you're looking for in this case. See https://github.com/AmpersandJS/ampersand-state/blob/master/ampersand-state.js#L93-L98 It allows you to transform incoming props.

ExtJS 4 - Model containing other model without Id relation

Given is a nested model structure like this:
Model Website
+ id
+ name
+ images[] // List of Image instances
Model Image
+ imageName
+ imageUrl
A serialised version of the response looks like:
{
"id": 4711,
"name": "Some name",
"images" [
{"imageName": "Beach", "imageUrl": "http://example.com/whatever.jpg"},
...
]
}
This nested model set is persisted in a document store and is returned on request by Website.id.
There is no by-id-relation to the nested list of images, as they are persisted as a list directly in the parent model. As far as I know, the classic relations in Ext.data.Model refer to the related models via a by-id-relation.
The question is: Is there any way that I can tell the parent model to use the Image model for each of the children in it's images list?
As a first step, you can make your images data to be loaded into the model by using a field type of auto:
Ext.define('My.Model', {
extend: 'Ext.data.Model'
,fields: [
{name: 'images', type: 'auto'}
// ... other fields
}
});
Then:
myModel.get('images');
Should return:
[
{"imageName": "Beach", "imageUrl": "http://example.com/whatever.jpg"},
...
]
From there, you should theoretically be able to implement a fully automatized solution to creates the models from this data, and -- the hardest part -- try to keep these created records and the children data in the parent model synchronized. But this is a very involved hack, and a lot of entry points in Ext code base have to be covered. As an illustration, I once tried to do that for "has one" relations, and that represent a lot of code. As a result, I never took the time to consolidate this code, and finally never used it.
I would rather advocate for a simple and local (to the model) solution. You can add a simple method to your model to get the images as records. For example:
Ext.define('My.Model', {
// ...
,getImages: function() {
var store = this.imageStore;
if (!store) {
store = new Ext.data.Store({
model: 'My.ImageModel'
,data: this.get('images') || []
});
this.imageStore = store;
}
return store;
}
});
Creating a store for the associated model will save you from having to play with the proxy and the reader. It also gives you an interface that is close to Ext's default one for associations.
If you need support for loading images more than once for the same parent record, you can hook on the field's convert method.
Finally, you may also need to handle client-side modifications of associated data, in order to be able to save them to the server. If your associated model allows it, you could simply use the children store's sync method (and don't forget to update the parent model's data in the sync callback!). But if your associated model isn't connected to an endpoint on the server-side, you should be able to hook on the serialize method to save the data in the associated store (as opposed to the one stored in the parent record, that won't get updated if you work with the associated store).
Here's a last example showing both:
Ext.define('My.Model', {
extend: 'Ext.data.Model'
,fields: [
{
name: 'images'
,type: 'auto'
// enables associated data update
,convert: function(data) {
var store = this.imageStore;
if (store) {
store.loadData(data || []);
}
return data;
}
// enables saving data from the associated store
,serialize: function(value, record) {
var store = record.imageStore,
if (store) {
// care, the proxy we want is the associated model's one
var writer = store.proxy && store.proxy.writer;
if (writer) {
return Ext.Array.map(store.getRange(), function(record) {
return writer.getRecordData(record);
});
} else {
// gross implementation, simply use the records data object
return Ext.pluck(store.getRange(), 'data');
}
} else {
return record.get('images');
}
}
}
// ... other fields
}
,getImages: function() {
var store = this.imageStore;
if (!store) {
store = new Ext.data.Store({
model: 'My.ImageModel'
,data: this.get('images') || []
});
this.imageStore = store;
}
return store;
}
});
Please notice that I haven't tested this code, so it might still contains some mistakes... But I hope it will be enough to give you the general idea!

Resources