I am adding facebook social plug in to a webpage
when I manually add :
<div class="fb-comments" data-href="http://website.com/z" data-width="700" data-numposts="7" data-colorscheme="light"></div>
it works , however , when javascript code add it , it doesn't
any ideas ?
The JS SDK goes through your document once when it is initialized, to look for such elements to parse into social plugins. If you want it to also parse content that you add to the document later, you need to call FB.XFBML.parse().
https://developers.facebook.com/docs/reference/javascript/FB.XFBML.parse/
That was great #CBroe!
Thank you! It worked in a Nextjs/React project.
As a reference, please, see a custom hook implementing it:
import { useEffect } from 'react';
let FB;
const useFacebook = ({ addTrack }) => {
useEffect(() => {
const facebookScript = document.createElement("script");
facebookScript.id = 'fb-sdk';
facebookScript.async = true;
facebookScript.defer = true;
facebookScript.crossOrigin = "anonymous";
facebookScript.nonce = "5JOEwLPT";
const track = addTrack ? `&appId=${process.env.NEXT_PUBLIC_FACEBOOK_ID}&autoLogAppEvents=1` : '';
facebookScript.src = `https://connect.facebook.net/pt_BR/sdk.js#xfbml=1&version=v10.0${track}`;
document.body.appendChild(facebookScript);
const startScript = document.createElement('script');
const code = `window.fbAsyncInit = function() {
FB.init({
appId : '${process.env.NEXT_PUBLIC_FACEBOOK_ID}',
autoLogAppEvents : ${addTrack},
xfbml : true,
version : 'v10.0'
});
};`;
startScript.appendChild(document.createTextNode(code));
document.body.appendChild(startScript);
if(window.FB) {
window.fbAsyncInit();
}
return () => {
document.body.removeChild(facebookScript);
document.body.removeChild(startScript);
}
}, [addTrack]);
};
export default useFacebook;
Full code Nextjs Facebook SDK comments sample:
https://github.com/cmdaniel/nextjs-facebook-sdk
Related
I'm having a great time building my blog with Svelte, but I'm switching the structure to to be accessed through a JSON API.
Right now it's easy to get the markdown metadata and path, but I'd love to also get the content.
How would I modify this posts.json.js file to also get the content?
const allPostFiles = import.meta.glob('../blog/posts/*.md')
const iterablePostFiles = Object.entries(allPostFiles)
const allPosts = await Promise.all(
iterablePostFiles.map(async ([path, resolver]) => {
const { metadata } = await resolver()
const postPath = path.slice(2, -3)
return {
meta: metadata,
path: postPath
}
})
)
const sortedPosts = allPosts.sort((a, b) => {
return new Date(b.meta.date) - new Date(a.meta.date)
})
return {
body: sortedPosts
}
Install and enable the vite-plugin-markdown
// svelte.config.js
import { plugin as markdown, Mode } from "vite-plugin-markdown";
/** #type {import('#sveltejs/kit').Config} */
export default {
kit: {
vite: {
plugins: [markdown({ mode: Mode.HTML })],
},
},
};
then the content will be available as html and frontmatter data as attributes
iterablePostFiles.map(async ([path, resolver]) => {
const { attributes, html } = await resolver();
return {
attributes,
html,
path: path.slice(2, -3),
};
})
(I suggest adding the metadata into the markdown files via frontmatter )
The answer above works perfectly, but it also works to tweak the API with this code:
const allPosts = await Promise.all(
iterablePostFiles.map(async ([path, resolver]) => {
const { metadata } = await resolver()
// because we know every path will start with '..' and end with '.md', we can slice from the beginning and the end
const postPath = path.slice(2, -3)
const post = await resolver()
const content = post.default.render()
return {
meta: metadata,
path: postPath,
text: content
}
})
)
The important addition is this:
const post = await resolver()
const content = post.default.render()
using these variable chains to avoid using the JS reserved word default.
So I am using the DraftJS package with React along with the mentions plugin. When a post is created, I store the raw JS in my PostreSQL JSONField:
convertToRaw(postEditorState.getCurrentContent())
When I edit the post, I set the editor state as follows:
let newEditorState = EditorState.createWithContent(convertFromRaw(post.richtext_content));
setEditorState(newEditorState);
The text gets set correctly, but none of the mentions are highlighted AND I can't add new mentions. Does anyone know how to fix this?
I am using the mention plugin: https://www.draft-js-plugins.com/plugin/mention
to save data
function saveContent() {
const content = editorState.getCurrentContent();
const rawObject = convertToRaw(content);
const draftRaw = JSON.stringify(rawObject); //<- save this to database
}
and retrieval:
setEditorState(()=> EditorState.push(
editorState,
convertFromRaw(JSON.parse(draftRaw)),
"remove-range"
););
it should preserve your data as saved.
the example provided (which works ok) is for inserting a new block with mention, saving the entityMap as well.
mentionData is jus a simple object {id:.., name:.., link:... , avatar:...}
One more thing:
initialize only once:
in other words do not recreate the state.
const [editorState, setEditorState] = useState(() => EditorState.createEmpty() );
und then populate something like:
useEffect(() => {
try {
if (theDraftRaw) {
let mtyState = EditorState.push(
editorState,
convertFromRaw(JSON.parse(theDraftRaw)),
"remove-range"
);
setEditorState(mtyState);
} else editorClear();
} catch (e) {
console.log(e);
// or some fallback to other field like text
}
}, [theDraftRaw]);
const editorClear = () => {
if (!editorState.getCurrentContent().hasText()) return;
let _editorState = EditorState.push(
editorState,
ContentState.createFromText("")
);
setEditorState(_editorState);
};
I'm new to React Hooks and honestly I'm not sure if this problem is related to Hooks or if I'm just doing something generally wrong.
I want to build a image uploader comonent that uses the HTML5 FileReader in order to show users the uploaded images before actually POSTing them.
Below is what I have so far.
Basically <div id="from-effect"></div> is currently my way of checking whether the images could be rendered.
I first wanted to fill this <div> without side effects (like <div>I have {files.length} files</div>) but this didn't react to changes at all.
The solution below with useEffect is reacting to changes.
However, if you try uploading a few images you will notice that quite often it's showing wrong results.
function FileUploader(props) {
const [files, setFiles] = useState([]);
const loadImageContent = (name, newFiles) => {
return (e) => {
newFiles.push({ name: name, src: e.target.result });
};
}
const handleUpload = async (e) => {
const newFiles = [];
for (const file of e.target.files) {
const reader = new FileReader();
reader.onload = loadImageContent(file.name, newFiles);
await reader.readAsDataURL(file);
}
setFiles(newFiles);
}
useEffect(() => {
console.log('in use Effect, files:', files);
const prevCont = document.getElementById("from-effect");
prevCont.innerHTML = `I have ${files.length} files`;
});
return <div>
<input
type="file" name="fileUploader" id="fileUploader"
accept="image/*" multiple="multiple"
onChange={handleUpload}
/>
<div id="from-effect"></div>
</div>;
}
What am I doing wrong?
Or even better, how can I implement this without side effects?
I am not sure I follow your ultimate goal, or what you mean when you say you want to show users the uploaded images before POSTing them - do you want to POST automatically, or do you want the user to click an "upload/save/POST" button or something?
Here is an example of how to display images:
Edit: made things a little more clear, added "save" button which shows an alert that contains data you could possibly use to POST back to your server. Also, added a method to "JSONify" the file metadata, since the way we are uploading files does not let us natively convert [object File] into JSON.
const { useState } = React;
function FileUploader(props) {
const [files, setFiles] = useState([]);
const getFileMetadata = file => {
/**
* The way we are handling uploads does not allow us to
* turn the uploaded [object File] into JSON.
*
* Therefore, we have to write our own "toJSON()" method.
*/
return {
lastModified: file.lastModified,
name: file.name,
size: file.size,
type: file.type,
webkitRelativePath: file.webkitRelativePath
}
}
const handleUpload = e => {
let newstate = [];
for (let i = 0; i < e.target.files.length; i++) {
let file = e.target.files[i];
let metadata = getFileMetadata(file);
let url = URL.createObjectURL(file);
newstate = [...newstate, { url, metadata }];
}
setFiles(newstate);
};
const handleSave = () => {
alert(`POST Files Here..\n\n ${JSON.stringify(files,null,2)}`);
}
return (
<div>
<input type="file" accept="image/*" multiple onChange={handleUpload} />
<div>
<button onClick={handleSave} disabled={!(files && files.length > 0)}>
Save Image(s)
</button>
</div>
{files.map(f => {
return (
<div>
<img src={f.url} height="100" width="100" />
</div>
);
})}
</div>
);
}
ReactDOM.render(<FileUploader />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
I am trying to dynamically add css to my html head using angular js. Here is sample code
<div ng-repeat="stylesheet in stylesheets">
<link href="/myapp/{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" media="{{stylesheet.media}}" title="{{stylesheet.title}}" />
</div>
This code works as expected, but when browser loads the page, it tries to fetch css resources with raw angularjs templates and I see "404 not found error" in network tab of firebug.
Eg: request http://localhost:8080/myapp/%7B%7Bstylesheet.href%7D%7D, status 404
When page is completely loaded, it does substitution of template values and loads proper css.
Is there a way to avoid 404 error and make it load css after angularjs processing?
You should use ng-href instead of href.
<link ng-repeat="stylesheet in stylesheets" ng-href="{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" />
Example
I made a AngularJS service to use easily the #Artem solution.
It's here on GitHub.
There's a another option using $route.resolve and promises. This will wait until the CSS is actually loaded not only added to the head (after that the browser just starts retrieving the file and depending on CSS size can cause page reflow).
// Routing setup
.config(function ($routeProvider) {
$routeProvider
.when('/home', {
controller: 'homeCtrl',
templateUrl: 'home.tpl.html'
}).when('/users', {
controller: 'usersCtrl',
controllerAs: 'vm',
templateUrl: 'users.tpl.html',
resolve: {
load: ['injectCSS', function (injectCSS) {
return injectCSS.set("users", "users.css");
}]
}
}).otherwise({
// default page
redirectTo: '/home'
});
})
Service implementation
.factory("injectCSS", ['$q', '$http', 'MeasurementsService', function($q, $http, MeasurementsService){
var injectCSS = {};
var createLink = function(id, url) {
var link = document.createElement('link');
link.id = id;
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
return link;
}
var checkLoaded = function (url, deferred, tries) {
for (var i in document.styleSheets) {
var href = document.styleSheets[i].href || "";
if (href.split("/").slice(-1).join() === url) {
deferred.resolve();
return;
}
}
tries++;
setTimeout(function(){checkLoaded(url, deferred, tries);}, 50);
};
injectCSS.set = function(id, url){
var tries = 0,
deferred = $q.defer(),
link;
if(!angular.element('link#' + id).length) {
link = createLink(id, url);
link.onload = deferred.resolve;
angular.element('head').append(link);
}
checkLoaded(url, deferred, tries);
return deferred.promise;
};
return injectCSS;
}])
You could add a timeout using tries if this is something you would like to include.
Check out this post for more details:https://medium.com/angularjs-meetup-south-london/angular-dynamically-injecting-css-file-using-route-resolve-and-promises-7bfcb8ccd05b
I've created a very simple example of how make a conditionaly css addition
<link rel="stylesheet" ng-if="lang_direction == 'rtl'" ng-href="{{lang_direction == 'rtl' ? 'css/rtl.css' : ''}}" >
For anyone wishing to create truly dynamic CSS at runtime with AngularJS this is what I used.
index.html
<head>
<style type="text/css" ng-bind-html="styles"></style>
</head>
cssService
this.$rootScope.myStyles = ".test { color : red; }";
This is just an example, it may be better for you to put the styles into an indexController if you have one and keep it off the $rootScope
I'm new to node.js, and attempting to use weld to render templates on the server-side and using express as the router.
However the examples for node.js doesn't show serving the content, and am fuzzy on how this would work with express:
var fs = require('fs'),
jsdom = require('jsdom');
jsdom.env(
'./test.html',
['./jquery.js', './weld.js'],
function(errors, window) {
var data = [{ name: 'hij1nx', title : 'code slayer' },
{ name: 'tmpvar', title : 'code pimp' }];
window.weld(window.$('.contact')[0], data);
}
);
Help or example would be appreciated.
I think something like this would work. Haven't tested though.
var fs = require('fs'),
jsdom = require('jsdom'),
app = require('express').createServer();
app.get('/', function(req, res) {
jsdom.env('./test.html', ['./jquery.js', './weld.js'], function(errors, window) {
var data = [{
name : 'hij1nx',
title : 'code slayer'
}, {
name : 'tmpvar',
title : 'code pimp'
}];
window.weld(window.$('.contact')[0], data);
res.send(window.document.innerHTML); //after the welding part we just send the innerHTML
window.close(); // to prevent memory leaks of JSDOM
});
});
app.listen(3001);