Configuring React Router SubRoutes with React-Semantic-UI CardGroups - react-router

I have researched this for a while, and followed documentation from the following places:
This issue on github led me to the semantic-ui-react documentation on Augmentation,
and the react-router documentation on setting up route config with sub-routes.
My route configuration seems to work, but it is unclear where to specifically implement the component augmentation.
I have tried <Card.Group items={items} as={Link} to={items.map((item,i)=>(item.href))} />, as well as trying to put the as={Link} in the props itself, and have faced a bunch of errors and eventually recursive failure leading to the dreaded Maximum call stack size exceeded!
For now, I am just hard coding the paths to components with react-router. A functional approach would be more elegant, and much more react-y, and this is what I'm after!

You trying to apply augmentation to Card.Group, while in fact you want you want to apply it to Card.
So correct usage for the subcomponent API is:
<Card.Group>
<Card as={Link} to='/foo' />
<Card as={Link} to='/bar' />
</Card.Group>
Correct usage for the shorthand API is:
const items = [
{ as: Link, content: 'Foo', to: '/foo' },
{ as: Link, content: 'Bar', to: '/bar' },
]
<Card.Group items={items} />

Related

Issues with text rendering in react component

So I am working with an API, and found this in the documentation:
let message = {
"text" : "<i>or</i> HN: the Next Iteration<p>I get the impression that with Arc being released a lot of people who never had time for HN before are suddenly dropping in more often. (PG: what are the numbers on this? I'm envisioning a spike.)<p>Not to say that isn't great, but I'm wary of Diggification. Between links comparing programming to sex and a flurry of gratuitous, ostentatious adjectives in the headlines it's a bit concerning.<p>80% of the stuff that makes the front page is still pretty awesome, but what's in place to keep the signal/noise ratio high? Does the HN model still work as the community scales? What's in store for (++ HN)?",
"time" : 1203647620
}
So in react (using function component), what is the proper way of rendering message.text? I am asking because my rendering output still has the tag <i></i> and <p></p> in the text when I return {message.text}. So I guess there is something wrong.
If you want to render a string containing html you can use dangerouslySetInnerHTML
In your case will be:
<div dangerouslySetInnerHTML={{ __html: message.text }} />
Watch out using this attribute because it could expose your app to XSS attacks. (check https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml)

Display Images on HTML using MongoDB, But it didn't show

I have the code backend using Node.js and Front end in HTML. I tried to get the image stored in mongo to front end. But in HTML it doesn't shows the image. But when I paste the binary data of image in img src tag it works. Help plz.
index.js
function loadImages() {
let isbn=''
let imgSource=''
if (CURRENT_URL.includes('#')) {
isbn = CURRENT_URL.substr(CURRENT_URL.indexOf('#') + 1,
CURRENT_URL.length);
console.log(isbn);
}
axios.get(baseUrlLocal + '/book/image/'+isbn)
.then(response => {
console.log(response.data)
document.getElementById('imgSource')
.setAttribute(
'src', 'data:image/png;base64,' +
btoa(unescape(encodeURIComponent(response.data))) +"'"
);
})
.catch(err => {
console.log(err);
});
}
HTML
<div class="card-body" id="image-src">
<img id="imgSource" src="" alt="Red dot" />
</div>
I don't think you can do this in node.js
document.getElementById('imgSource')
.setAttribute(
'src', 'data:image/png;base64,' +
btoa(unescape(encodeURIComponent(response.data))) +"'"
);
document object is available in your browser mate. In node js you can't just use this like that. If you want to render the image you have processed, then you might want to look for a front end template engine
like
ejs
pug
handlebars
since ejs's syntax is a little bit annoying and can be very confusing sometimes you can use an alternative like handlebars take a look at here
handle bars is like a template engine. If you are familiar with Laravel or ASP.NET's Razor blade or Angular's string interpolation techniques this is very much like that so
go to your shell and install handlebars like this
npm install --save handlebars
since this is a little too much of a topic to discuss as an answer I'll just provide you with this link a tutorial on how to install and use handle bars. try that mate you will be able to achieve what you are looking for.. Cheers ...

To change the value of the URL in the browser, but not the React component to be rendered

I have a link <Link to = "country/USA"/>
Then the route <Route path="country/:countryId" component={Country}/>
When the component "Country" is rendered, the browser Url is
http://<domain>/country/USA
Is there a way to change the display of the browser Url as http://<domain>/country, and have the "countryId" only as the Route props in the Country component.
Have gone through
https://reacttraining.com/react-router/web/api/BrowserRouter
https://github.com/ReactTraining/react-router
Couldn't find anything helpful.
Any suggestions?
Thanks!
Unless you're okay with USA being part of a query string, there's nothing much you can do within React Router (snippet comes from react-training site)
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true }
}}/>
Another alternative that doesn't involve router is to have a Container component that receives USA as props and then programatically render the component that you want. I'd say that's way too much work for something that makes sense to be shown in the URL
EDITED
One way to do it via component would be something like this:
<Route path="country/some-static-keyword" render={() => <Country countryId={this.props.countryId}/>}/>

How would you embed atomic partials into a template data object

I'm using handlebars and assemble with yeoman and gulp.
I want to have some globalized partials that are able to be nested or injected into another partial by calling it within the context of a data object.
A simple example of that would be having a list of links that I could reference inside content throughout the site. The reason behind this, is the need for consistency. If for example, if I have a link within text on a page that I reference a 15 times throughout an entire website, but then realize I need to add a trade mark or modify the text, I want to update it once, not 15 times.
This is an example of what I want to do. Define global data inside a json file:
links.json
{
"apple": {
"linktext": "apple",
"target": "_blank",
"href": "http://www.apple.com"
},
"blog-article-foo-bar": {
"linktext": "foo bar",
"href": "http://www.foobar.com"
},
"dell": {
"linktext": "dell",
"target": "_parent",
"href": "http://www.dell.com"
}
}
Generate a partial from that content using a simple or complex template:
links.hbs
<a href="{{href}}" {{#if target}}target="{{target}}"{{/target}}>{{linktext}}</a>
And be able to embed that partial into another one by referencing it some how. This didn't work, but I've been reading about custom helpers, but can't figure out how I would intercept the partial and bind it into the other partial.
text.json
{
"text": "If you need a computer, go to {{> link link.apple}}."
}
text.hbs
<p>
{{text}}
</p>
compiled.html
<p>
If you need a computer, go to apple.
</p>
If you have suggestions or examples that might help me understand how to achieve this, I'd really appreciate the support. Thanks in advance.
There is some information about Handlebars helpers in their docs but not that much.
Since you're trying to use handlebars syntax in the value of a property on the context (e.g. text), handlebars won't render the value since it's already rendering the template. You can create your own helper that can render the value like this:
Handlebars.registerHelper('render', function(template, options) {
// first compile the template
const fn = Handlebars.compile(template);
// render the compiled template passing the current context (this) to
// ensure the same context is use
const str = fn(this);
// SafeString is used to allow HTML to be returned without escaping it
return new Handlebars.SafeString(str);
});
Then you would use the helper in your templates like this:
{{render text}}
Thanks for the example #doowb, your code did work but not for what I was trying to do. I really wanted something more complicated but I simplified my question not knowing it would be an issue. The code you provided worked (I think after a slight tweak) for a simple render of a template, but my templates use helpers such as #each and #if which caused the issue. Where the helpers were in my template, I ended up getting async placeholders. For example: <a $ASYNC$1$3...> I later learned this has to do with how partials are rendered. Understanding that lead me to subexpressions and the below solution.
Keeping my example above with some modifications, this is how I was able to merge partials.
First, I simplified the placeholder in text.json to basically a unique ID, instead of trying to render the partial there.
On the hbs template that I'm rendering to, such as a page or whatever, I included the insert helper with 3 arguments. The first two are subexpressions, each return a flattened partials as strings. The key here is that subexpressions process and return a result before finishing the current process with the helper. So two flattened templates are then sent to the helper along with the placeholder to search for.
The helper uses the third argument in a regex pattern. It searches the second argument (flattened parent template) for this pattern. When found, it replaces each instance of the pattern with the first argument (yes its a global fine replace).
So, the flattened child string gets inserted into parent each time placeholder is found.
First argument
(partial "link" link.apple)
Returns
'apple'
Second argument
(partial "text" text.text-example)
Returns
'<p class="text font--variant">If you need a computer, go to {{linkToApple}}.</p>'
Third argument
'linkToApple'
text.json
{
"text-example": {
"elm": "quote",
"classes": [
"text",
"font--variant"
],
"text": "If you need a computer, go to {{linkToApple}}."
}
}
text.hbs
<{{elm}} class="{{#eachIndex classes}}{{#isnt index 0}} {{/isnt}}{{item}}{{/eachIndex}}">{{text}}</{{elm}}>
compile.hbs
{{insert (partial "link" link.apple) (partial "text" text) 'linkToApple' }}
compile.html
<p class="text font--variant">If you need a computer, go to apple.</p>
gulpfile.js
app.helper('insert', function(child, parent, name) {
const merged = parent.replace(new RegExp('\{\{(?:\\s+)?(' + name + ')(?:\\s+)?\}\}', 'g'), child);
const html = new handlebars.SafeString(merged);
return html;
});
Hope this helps someone else. I know this can use improvements, I'll try to update it when I get back to cleaning up my gulp file.

Material design lite inputs in Ember.js app loses it design after route transition

I am trying to use Material design lite with an Ember.js application and got the form working somehow. However, when the user navigates from one page to another page containing the form or inputs, the inputs do not seem to behave as expected. For an example here, when the page loads first time to home page, input works fine but when we switch between sign-in and home pages, inputs fallbacks to basic form and material design animation is lost.
Not sure if this issue is related to Ember.js or Material design but any help would be highly appreciated.
MDL requires elements to be initialized to get special effects such as buttons with ripples, or animated input boxes. They are initialized by default on page load, but elements inserted by views or components will not be initialized. The simplest approach is to initialize them on didInsertElement.
A more general approach would be a mixin which handles this for you, as in:
// mixins/mdl-button.js
export default Ember.Mixin.create() {
initializeMdlButtons: function() {
var buttons = this.get('element').querySelectorAll('.mdl-button');
[].forEach.call(buttons, button => componentHandler.upgradeElement(button));
}.on('didInsertElement')
Then in your component using buttons
import MdlButton from 'app/mixins/mdl-button';
export default Ember.Component.extend(MdlButton, {
...
});
Or, you could apply this to all components with
Ember.Component.reopen(MdlButton);
You will need to create handlers to initialize the required JS for each MDL component. You have two possibilities:
Use the JavaScript that comes with MDL, though it will become toublesome on some of the components.
Implement JS on your own per component, and use ideas of the JS internally in MDL.
I used 2. This is why I have written a ember-addon specifically to create ember components out of MDL.
It's pretty simple.
ember install ember-mdl
Demo / docs: http://peec.github.io/ember-mdl/
Example of implementation is in the dummy app
Or you can just do componentHandler.upgradeDom() on didInsertElement. Which based on their documentation
Searches existing DOM for elements of our component type and upgrades
them * if they have not already been upgraded.
initializeItems: function () {
componentHandler.upgradeDom();
}.on('didInsertElement')
Thanks #torazaburo for your suggestation. I had to modify mixin to get it working. In my case i have textfield input and needed to modify the mixing. Here is my solution if someone still needs.
// app/mixins/textfield-support.js
import Ember from 'ember';
export default Ember.Mixin.create({
initializeMdlTextfield: function() {
componentHandler.upgradeElement(this.get('element'), 'MaterialTextfield');
}.on('didInsertElement')
});
Then we can extend the mixing in our component as below.
// app/components/mdl-textfield-input.js
import Ember from 'ember';
import layout from '../templates/components/mdl-textfield-input';
import mdlTextfield from '../mixins/textfield-support';
export default Ember.Component.extend(mdlTextfield, {
tagName : 'div',
attributeBindings : ['disabled', 'type', 'name' ],
hasFloatingLabel : false,
containerClassNames : '',
labelText : null,
classNames : ['mdl-textfield', 'mdl-js-textfield'],
classNameBindings: [
'hasFloatingLabel:mdl-textfield--floating-label',
'containerClassNames'
],
layout
});
Component template would look something like this.
// app/templates/components/mdl-textfield-input.hbs
{{yield}}
{{input id=name value=value type=type disabled=disable classNames="mdl-textfield__input"}}
<label class="mdl-textfield__label" for={{name}}>{{labelText}}</label>
<span class="mdl-textfield__error">{{error}}</span>
And use this component as below.
{{mdl-textfield-input
name='username'
value=model.username
labelText='Username'
hasFloatingLabel=true
type='text'
containerClassNames ='mdl-cell--12-col'
}}