Ember - Sideloading data within RESTAPISerializer - rest

I am trying to sideload data with RESTAPISerializer, but there seems to be some problem.
The goal is to store articles with tags. The articles appear in the store just fine, and I can look at them with Ember inspector. But I can't seem to figure out how to store the tags, they never show up in the inspector.
I created a model called blog-tag to store the tags. They come in a specific format, which I can't change, and it looks like this:
tid:term
... and I am getting an array of them with each article.
The article model includes a hasMany relationship with blog-tags. I am trying to pass them in through the serializer, and have been using various formats (JSON, javascript arrays, etc.) I wrote a custom serializer for blog tags as well, but it doesn't seem to do much.
Can someone explain what I am missing?
The article model:
import DS from 'ember-data';
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
title: attr(),
blogTags: hasMany('blog-tag', { async: true })
});
The blog-tag model:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
tid: attr('number'),
term: attr('string'),
});
The article serializer:
import RESTAPISerializer from 'ember-data/serializers/rest';
export default RESTAPISerializer.extend({
normalizeResponse: function(store, primaryModelClass, payload, id, requestType) {
var normalizedPayload = {articles: []};
payload.forEach(function(item) {
var tags = item.tags.split("|"), blogTags = [];
// storing blog tags in an array, have also done it as a
// JSON structure
var blogTags = tags.map((tag) => {
var item = tag.split(":"),
vals = {};
vals.tid = item[0];
vals.term = item[1];
return vals;
});
var article = {
id: item.nid,
title: item.title,
blogTags: blogTags,
};
normalizedPayload.articles.push(article);
});
return this._super(store, primaryModelClass, normalizedPayload, id, requestType);
},
});

Solved my own problem. Answering the question for the benefit of anyone else running into the same issue.
What I was trying to do is embed the tags from the articles serializer. This mean I needed to use the EmbeddedRecordsMixin.
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
It was actually pretty simple to do this. Made the following changes to the serializer described above, the code worked as-is with these additions.
// app/serializers/articles.js
import RESTSerializer from 'ember-data/serializers/rest';
import EmbeddedRecordsMixin from 'ember-data/serializers/embedded-records-mixin';
export default RESTSerializer.extend(EmbeddedRecordsMixin, {
attrs: {
blogTags: { embedded: 'always' }
}
});

Related

How to import type definition from react-query to set type of options for useQuery hook?

I'm adding react-query to my automations pipe and need generating wrapper around the useQuery for various API calls. I want to expose all options of useQuery to developer using this wrapper. How to correctly define options type to get all the benefits of ts for those?
:any works but not giving autocomplete/checks then:
const usePosts = ({ page, perPage, workspaceId }: payload, ___options?: any) => {
If I add :UseQueryOptions, just to see the error of mismatch, error message is quite big and has a lot inside. Would love to find way to import that type definition from react-query and reuse it.
Just found it, sometimes all needed is to write a question 😅 Maybe someone else will benefit from this:
import { useQuery, QueryOptions } from "#tanstack/react-query";
import sdk from "../sdks/demo";
type payload = {
page?: number;
perPage?: number;
workspaceId: string;
};
const usePosts = (
{ page, perPage, workspaceId }: payload,
___options?: QueryOptions
) => {
let key = `posts-list-${workspaceId}-${page}-${perPage}`;
return useQuery(
[key],
() =>
sdk.postsList({
workspaceId,
page,
perPage,
}),
___options
);
};
export default usePosts;

Svelte/SvelteKit: Dynamic import of components with variable

I want to dynamically import components without importing a specific component.
I want to set the component name with a variable, received from the store:
<script lang="ts">
// SVELTE
import { onMount } from 'svelte';
// STORE
import { dynamicComponent } from '$stores/dynamicTitle';
$: $dynamicComponent;
console.log($dynamicComponent)
let renderDynamicComponent
onMount(async () => {
const importValue = (await import(`../../lib/components/Home/DynamicComponents/${String($dynamicComponent)}.svelte`)).default;
// const importValue = (await import(`../../lib/components/Home/DynamicComponents/IntroSectionCustom.svelte`)).default;
renderDynamicComponent = importValue
});
<svelte:component this={renderDynamicComponent}/>
But I get:
Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: http://localhost:3000/src/lib/components/Home/DynamicComponents/Intro-Section-Custom.svelte
I do not understand. From the error, it seems to be the right path ...
The Rollup plugin #rollup/plugin-dynamic-import-vars might be of help here. I haven't used it with SvelteKit specifically, but it worked fine with standard Svelte with Vite as bundler.
// Example.svelte
function importLocale(locale) {
return import(`./locales/${locale}.js`);
}
// vite.config.js
import dynamicImportVars from '#rollup/plugin-dynamic-import-vars';
export default (mode) =>
defineConfig({
plugins: [
dynamicImportVars({
include: './src/Example.svelte'
})
]
});
SvelteKit uses Vite behind the scenes, but has its own configuration format. In svelte.config.js, pass dynamicImportVars() to the config.vite.plugins key:
// svelte.config.js
/** #type {import('#sveltejs/kit').Config} */
const config = {
vite: {
plugins: [
dynamicImportVars({
include: './src/Example.svelte'
})
]
}
};
export default config;
Please take note of the limitations mentioned in the README of the Rollup plugin.
I don't think Svelte + the bundler currently support dynamically generated paths:
let thing = 'Thing';
Thing = (await import(`./${thing}.svelte`)).default; // this won't work
Thing = (await import(`./Thing.svelte`)).default; // this will work
limitation of the bundler.
github issue: https://github.com/sveltejs/svelte/issues/6702
What you're doing does work if not using import vars. When adding an import variable you need make your renderDynamicComponent identifier reactive. So instead of this:
let renderDynamicComponent
Do this:
$: renderDynamicComponent = null
This will allow svelte:component to render your imported component with dynamic path variable. This seems to be a special case when using dynamic import vars with Vite.

How to query collections from custom plugin in strapi?

I want to collect data from my collections and display it in my own plugin, for example 'Cars'. I have not found anything about this and do not know how to approach this.
import React, { memo } from 'react';
import pluginId from '../../pluginId';
const HomePage = () => {
const fetchData = () => {
// Here I want to fetch data from my collection and display it
return null;
}
return (
<div>
<h1>{pluginId}&apos;s HomePage</h1>
<p>Happy coding</p>
{fetchData()}
</div>
);
};
export default memo(HomePage);
Old question but I've been looking for the answer and it's difficult to find.
So the solution for this, is to use the endpoints provided by the content-manager plugin of strapi.
First you should go and allow public access to this endpoints in Settings then Roles & Permissions plugin.
Finally you can query your data like this
const response = await request("/content-manager/collection-types/application::cars.cars", {
method: "GET"
});
}
Case : Api model :
const cars = await strapi.query('car').find({});
Case : Plugin model :
const cars = await strapi.query('car', 'plugin_name').find({});

Algolia: How to pass refinements from a front-end to a backend?

I have a webpage that uses Algolia's React InstantSearch. It has a search bar and several refinements.
I want the user to be able to press a button and get a list of all matching results.
To get a list of all results, I need to use the Browse Index instead of the Search Index. The Browse Index allows retrieving all hits; the Search Index allows retrieval of only up to 1000 hits. However, the Browse Index should not be used in UIs. So I want to create an API endpoint my web server that uses the Browse Index in order return a list of matching hits given a search query.
I am able to successfully do this for a search query, but I can't figure out how to this for refinements.
Here is a sketch of what I have so far.
Back-end (in Ruby):
ALGOLIA_INDEX = Algolia::Index.new('Products')
class AlgoliaSearchController < ActionController::Base
def get_search_results
query = params['query']
hits = []
ALGOLIA_INDEX.browse({query: query}) do |hit|
hits << hit
end
render json: hits
end
end
Frontend:
import qs from 'qs';
import React, { useCallback, useState } from 'react';
import { InstantSearch } from 'react-instantsearch-dom';
function getSearchResults(query) {
const queryString = qs.stringify({
query,
})
return fetch(`/search_results?{queryString}`);
}
function App() {
const [searchState, setSearchState] = useState(null);
const onSearchStateChange = useCallback(searchState => {
setSearchState(searchState);
}, [searchState]);
const onClick = useCallback(() => {
console.log(getSearchResults(searchstate.query));
});
return (
<InstantSearch ... onSearchStateChange={onSearchStateChange}>
<button onClick={onClick}>Search</button>
</InstantSearch>
);
}
I can't find any resources that explain how to do search with refinements.
Things I've looked at so far:
I can try to map the searchState format to the Search API Parameters used by the Browse Index. I could write my own mapper from search state to a query, however, 1) this seems complex and I suspect I'm missing something simpler and 2) this seems like this should be open-sourced somewhere since I suspect I'm not the first to run into this issue.
There is an article, Backend InstantSearch
, that explains how to write a backend that can be plugged into the InstatSearch component. However it doesn't explain how I could do a one-off search from the search state.
You are right that this is currently not exactly straightforward. The flow to get the raw search parameters you can use for "browse" is like this:
give your custom component access to the last search results
read the state from those results
create a new helper using this state
use helper.getQuery() to get the query parameters to apply
A sandbox that illustrates this is: https://codesandbox.io/s/extending-widgets-luqd9
import React, { Component } from 'react';
import algoliaHelper from 'algoliasearch-helper';
import { connectStateResults } from 'react-instantsearch-dom';
class Downloader extends Component {
state = {
instructions: '',
};
onDownloadClick = () => {
// get the current results from "connectStateResults"
const res = this.props.searchResults;
// read the private "state" (SearchParameters) from the last results
const state = res && res._state;
// create a new "helper" with this state
const helper = algoliaHelper({}, state.index, state);
// get the query parameters to apply
const rawQuery = helper.getQuery();
this.setState({
instructions:
'Do a search backend with this:\n\nclient.browseAll(' +
JSON.stringify(rawQuery, null, 2) +
')',
});
};
render() {
return (
<React.Fragment>
<button onClick={this.onDownloadClick}>download</button>
<pre>{this.state.instructions}</pre>
</React.Fragment>
);
}
}
export default connectStateResults(Downloader)

Custom PhoneGap Plugin (iOS) Function Issue

I'm using this tutorial to create a custom PhoneGap plugin:
http://wiki.phonegap.com/w/page/36753496/How%20to%20Create%20a%20PhoneGap%20Plugin%20for%20iOS
I have had success using the author's example, but I have a few questions that I have not been able to find out the answers to.
When the JavaScript function is created, the code is:
var MyPlugin = {
nativeFunction: function(types, success, fail) {
return PhoneGap.exec(success, fail, "PluginClass", "print", types);
}
};
Is there a way to set this up without var MyPlugin = {...}; and nativeFunction? In other words, can we define a function of our plugin like myfunc = function()...
Secondly, assuming there is a way to do the above, could this code:
MyPlugin.nativeFunction(
["HelloWorld"] ,
function(result) {
alert("Success : \r\n"+result);
},
function(error) {
alert("Error : \r\n"+error);
}
);
(which is the test code to test the plugin) also be written in a more standardized way? I.e., just a call to Javascript function without the nativeFunction part?
I would very much appreciate any input, thank you!
the phonegap documentation for plugins sucks. Honestly I had a bunch of issues when trying to create my own. A few tips :
the reason for doing
var MyPlugin = {};
is because this allows us to us scope things specific to that js object.
example:
MyPlugin.myFunction();
My favorite method to create plugins, similar to your question, is to prototype them
var MyPlugin = {}; // our object
MyPlugin.prototype.myFunction = function(success,fail,types){
}
The key to making a plugin fire is this -
PhoneGap.exec(success,fail,"MyPlugin","myFunction",types);
But something that they leave out is, what if we want to have options to our plugin? What if we want to do more than pass a string, then the example doesn't work. The fix is easy but not talked about at all.
var MyPlugin = {};
MyPlugin.prototype.myFunction = function(success,fail,options){
var defaults = {
foo: '', // these are options
bar: '',
};
// this parses our "options"
for(var key in defaults) {
if(typeof options[key] !== "undefined") defaults[key] = options[key];
}
return PhoneGap.exec(success,fail,"MyPlugin","myFunction",[defaults]);
}
when we call this with out javascript -
var foo = MyPlugin.myFunction(success,fail,{
foo:'hello',
bar:'world'
});
You'll notice that most of the phonegap API uses this syntax, which I found strange that their documentation didn't really talk about how to do this.
I have a post about a plugin I create you can check it out for reference.
Blog - http://www.drewdahlman.com/meusLabs/?p=138
Git - https://github.com/DrewDahlman/ImageFilter