Render Isomorphic content fetched over AJAX - reactjs

I'm not new to React but not the most seasoned developer here either. Right now I'm burrowing myself in complex personal projects in order to learn all about what react has to offer and to get better at it.
I've hit a wall right now and I'd like to get some opinions on how to solve this issue:
I have a news-feed in my React+Redux app. It loads its content using FetchAPI and renders the content after that, like so:
componentWillMount: function() {
getNews() // Populates redux store (this.props.news)
},
render: function() {
if (!this.props.news.title) return <span>Loading...</span>
return (
<div className="news">
<div className="title">{this.props.news.title}</div>
<div className="image" style={this.style()}></div>
<div className="body">{this.props.news.body}</div>
</div>
)
}
This works just fine for a SPA but the problem arises when we try to render this isomorphic.
I use pollyfill so the node server understands the FetchAPI. When i load '/news' directly the node server has gotten the ajax data and renders the news page with the article preloaded. The client however complains about 'checksum was invalid.' which means it tried to render before it got the response.
I have read up on this issue and a lot of people think that for a SPA we need to show a 'Loading...' to improve UX. A lot of people also think we need to render this due to SEO & NON-JS reasons. I'd like to wait for the data on the server but not on the client. I'd also likt it if i did not get this problem with invalid checksums on page load where content is loaded asynchronously.
How should I go about this? I'd like not to write any
if (server) {wait()} else {renderLoading()}

Related

React: DOM manipulation from HTML response

I have an API which returns a whole block of HTML. In old project (JS, jQuery... not React) I had a bunch of jQuery events declared after I got this HTML so all the clicks were working fine.
The API returns some classes and ids like this:
<div id="content">
<span class="span">Span 1</span><span class="span">Span 2</span>
</div>
And simple events:
$('.span').click(function (e) {
$(this).text('Span clicked');
});
Obviously it's not that simple and it fills an entire page with different DOM events. Convert it to States is not that simple unless I modify completely the response and this is something that cannot be modified from backend (and from frontend it's too much).
What's the best way in React of doing this without copying the whole jQuery events?
(I don't find it clean)
There are more questions to be asked here but main response will be always to not bring jQuery to React.
As mentioned, there are some situations where we are sitting on the fence and we cannot wait here for a backend refactor.
Since I have React running on a old platform using JS + jQuery but I have done is move my jQuery logic to something global like this:
// NOTE: temporary function until API is refactored returning a json
window.runOldJquery = function () {
$('.span').click(function (e) {
$(this).text('Span clicked');
});
}
So in React, I call my API and when it successes I call window.runOldJquery.
This is the best solution I could find for this specific scenario without bringing nothing to React.

How are Javascript widgets made without iFrames?

I have a chat widget that I want to embed it other people's websites. It looks just like Intercom and all the other chat popups. I want to make the chat popup stick to the bottom-right hand corner of the screen regardless of where you scroll. However, when I import the chat app as an iframe and give it position: fixed; bottom: 0px; right: 15px;, the iframe does not go to where I expect it to go.
I realize that iframes are suboptimal for embedded JS widgets, and all the best embedded apps are importing .js files from file storage. After searching online for hours I have yet to find an explanation/tutorial on how to make those JS files that hook onto a and render the widget. How do you even make one of those pure javascript apps, and what are they called? (Not web components I assume, because there have been widgets for a long time).
Sorry if this question is kinda noob. I never knew this was a thing until I tried implementing it myself. Can anyone point me in the right direction on how to get started making JS web widgets? Thank you! (Maybe a ReactJS to VanillaJS converter would be super cool)
A pure Javascript App is called a SPA - Single Page Application - and they have full control over the document (page). But since you ask about embeding a widget, I don't think that is what this question is about (there are tons of info. on the web on SPAs).
I was going to suggest that going forward you do this using Web Components - there are polyfills available today that make this work on nearly all browsers - but since your question mentioned that you wanted to know how it is done without it, I detail below one of my approaches.
When creating a pure JS widget you need to ensure that you are aware that a) you do NOT have control over the global space and b) that it needs to play nice with the the rest of the page. Also, since you are not using Web Components (and are looking for a pure javascript (no libs)), then you also have to initialize the widget "manually" and then insert it to the page at the desired location - as oposed to a declaritive approach where you have an assigned HTML tag name for your widget that you just add to the document and magic happens :)
Let me break it down this way:
Widget Factory
Here is a simple Javascript Widget factory - the create() returns an HTML element with your widget:
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div")
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
To create a new widget (HTML Element) using the above you would:
const myWidgetInstance = Widget.create("chat-12345");
and to insert this widget into the page at a given location (ex. inside of a DIV element with id "chat_box", you would:
document.getElementById("chat_box").appendChild(myWidgetInstance);
So this is the basics of creating a Widget using the native (web) platform :)
Creating a reusable/embeddable Component
One of the key goals when you deliver a reusable and embeddable component is to ensure you don't rely on the global space. So your delivery approach (more like your build process) would package everything together in a JavaScript IIFD which would also create a private scope for all your code.
The other important aspect of these type of singleton reusable/embeddable components is that your styles for the Element needs to ensure they don't "leak" out and impact the remainder of the page (needs to play nice with others). I am not going into detail on this area here. (FYI: this also the area where Web Component really come in handy)
Here is an example of a Chat component that you could add to a page anywhere you would like it to appear. The component is delivered as a <script> tag with all code inside:
<script>(function() {
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div");
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
const myWidgetInstance = Widget.create("chat-12345");
const id = `chat-${ Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) }`;
document.write(`<div id="${ id }"></div>`);
document.getElementById(id).appendChild(myWidgetInstance);
})();</script>
So you could use this in multiple places just by droping in this script tag in the desired locations:
<body>
<div>
<h1>Chat 1</h1>
<script>/* script tag show above */</script>
</div>
...
<div>
<h1>Chat 2</h1>
<script>/* script tag show above */</script>
</div>
</body>
This is just a sample approach of how it could be done. You would have to add more in order to support passing options to each widget (ex. the chat id), defining styles as well other possible improvements that would make the runtime more efficient.
Another approach
You could add your "script" once and wait for the rest of the page to load, then search the document for a "known" set of elements (ex. any element having a CSS Class of chat-box) and then initialize a widget inside of them (jQuery made this approach popular).
Example:
Note how data attributes can be used in DOM elements to store more data specific to your widget.
<div class="chat-box" data-chatid="123"></div>
<script>(function() {
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div");
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
const initWhenReady = () => {
removeEventListener("DOMContentLoaded", initWhenReady);
Array.prototype.forEach.call(document.querySelectorAll(".chat-box"), ele => {
const myWidgetInstance = Widget.create(ele.dataset.chatid);
ele.appendChild(myWidgetInstance);
});
};
addEventListener('DOMContentLoaded', initWhenReady);
})();</script>
Hope this helps.
The best way to create Javascript widget without third-party library is to create Custom Elements.
The following link : Custom Elements v1 is a good introduction to this technology.
See a minimal example below:
class Chat extends HTMLElement {
connectedCallback () {
this.innerHTML = "<textarea>Hello</textarea>"
}
}
customElements.define( "chat-widget", Chat )
<chat-widget>
</chat-widget>

Trouble implementing google sso in AngularJS application

So I have been trying to implement google single sign on into my angular application; however, sometimes when I reload the page the button disappear. My angular application is using angular routing. If I were to put my button outside of this it would work as expected. It just runs into problem when its loaded through a partial. Any idea how I can fix this?
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<div ng-view></div>
As #agektmr said, the problem is related to the way angular and platform.js interact with each other.
In order to use the auto rendered button you need to trigger the library when the DOM is loaded.
What I did is calling the following code in the onComplete method (I'm working with AngularMaterial dialogs, but you should be able to find a similar method quite easily):
$timeout(function() {
$window.gapi.signin2.render('g-signin2');
});
The only difference is in your html you should change your div and instead of adding it a g-signin2 class you should add an g-signin2 id:
<div id='g-signin2' data-onsuccess='yourMethod'></div>
If you're willing to learn more about Google's implementation you could take a look here.
I'd recommend using imperative approach for implementing the button for this.
<div id="signin">
<button>sign-in</button>
</div>
<script>
document.querySelector('#signin').addEventListener('click', function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signIn();
});
</script>
Find more concrete example here
https://github.com/GoogleChrome/google-sign-in
The code you indicated didn't work because of timing. platform.js library tries to take care of it but fails because it's before angular renders DOM.

Node Webkit / Angular / Change Model

Sorry for a not very specific question by someone new to node webkit, new to Angular, new to about everything in web development:
My app is based on a JSON file that I load at the init of my node webkit app and which is at the center of a bunch of calculations.
In the app, one can open a file dialog to create a new JSON file. Now, of course, I would like the app to recalculate everything based on the new JSON. It works when I press the "refresh" button of node webkit, but I couldn't get it running by using either
require('nw.gui').Window.open('index.html');
nor
require('nw.gui').Window.get().reload(3);.
I am also wondering if handling this on the node level is the good way to do it. Shouldn't it rather be done by Angular? But I couldn't really connect to the content of my controller from an "outside" javascript.
Grateful for any hint...
Having logic on the page loading is always tricky and as you mentioned - requires page reloading what is not very elegant and modern applications avoid this.
In your case, I suggest that if your JSON file is not very big - store it in variable and modify it as needed. The elegant way will be to create Angular service, which can act as a "model".
angular.service('JsonService', function() {
var json = {
// content
};
return {
getJson: function () {
return json;
},
setJson: function (newJson) {
json = newJson;
}
};
});
Then, whenever you need to update JSON invoke setJson(newJson) method and modify your controllers to use the service getJson() method.
You can also add the loading/saving to file functions to this service. The loading function can be invoked in your main controller connected to your dashboard page. Then before the first page will be visible, the JSON file will be already loaded and you preserve desired behavior.

Add "intermediary" page prior to full page load Angular

My problem
I am using Angular to create a Phonegap application. Most of my pages are fairly small and the transition/responsiveness is quick and smooth. However I have one page that is fairly large that I am having an issue with.
The method for changing to this page is straightforward:
<button ng-click="$location.url('/page2')"></button>
When you "tap" the button above it takes about 1-2s to respond and change pages. I have double checked all areas for improvement on this page and determined that the delay is caused by Angular compiling and parsing the DOM of this page prior to changing the page. Please note that I am testing this on a real device so it is not due to emulator speeds.
The question
Is there a way to automatically or manually intercept page changes and put them in a sort of "loading" page so the response to the button click is immediate and page change is visible but the page content loads in a second or 2 later onto this "loading" page.
Its only an issue cause it is very awkward to click something and have nothing happen. I am having a very hard time finding any resources on this matter so if someone can even point me in the right direction to look I would be grateful.
Edit:
A super hacky solution I found was to use an ng-include on wrapper page and delay the include for a little bit.
myBigPageWrapper.html:
<div ng-include="page"></div>
Controller:
$scope.page = '';
setTimeout(function() { $scope.page='/pages/myBigPage.html'; $scope.$apply(); }, 1000);
Then navigate to your wrapper page instead: $location.url('/myBigPageWrapper')
This is obviously not ideal... But I hope this helps clarify what I am attempting to do.
Page2.html
This is the section that causes the page to slow down, commenting this out makes the page load very quickly. There are 13 pages in the "auditPages" array each containing about 50 lines of html mostly containing form input elements. Quite a bit of logic however it runs great once it is loaded. I am not going to include all the pages as it would be overload.
<div class="page-contents">
<form name="auditPageForm">
<div ng-repeat="(pageKey, pageData) in auditPages " ng-show="currentAuditPage.name==pageData.name">
<audit-form page="pageData">
<ng-include src=" 'partials/audit/auditSections/'+pageData.name+'.html'" onload="isFormValid(pageKey)"></ng-include>
</audit-form>
</div>
</form>
</div>
To sum up my comments above:
Your question was:
Is there a way to automatically or manually intercept page changes and
put them in a sort of "loading" page?
A lot of people asks for this question since Angular doesn't seem to provide a nice handling of a loading transition.
Indeed, the possible nicest solution would have been to "play" with the resolve property of angular's module configuration.
As we know, resolve allows to run some logic before the targeted page is rendered, dealing with a promise. The ideal would be to be able to put a loading page on this targeted page, while the resolve code is running.
So some people have nice ideas like this one:
Nice way to handle loading icon while route is changing
He uses $routeChangeStart event, so the loading icon would happen on the SOURCE page.
I use it and it works well.
Also, there is another way: make use of $http interceptor (like #oori answer above), to have a common code allowing to put a loading icon but...I imagine you don't want the same icon on every kind of http request the page does, it's up to you.
Maybe in the future, a solution would come directly associated to the resolve property.
Angular has $httpProvider.responseInterceptors
// Original by zdam: http://jsfiddle.net/zdam/dBR2r/
angular.module('LoadingService', [])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('myHttpInterceptor');
var spinnerFunction = function (data, headersGetter) {
angular.element(document.getElementById('waiting')).css('display','block');
return data;
};
$httpProvider.defaults.transformRequest.push(spinnerFunction);
}])
// register the interceptor as a service, intercepts ALL angular ajax http calls
.factory('myHttpInterceptor', ['$q','$window', function ($q, $window) {
return function (promise) {
return promise.then(function (response) {
angular.element(document.getElementById('waiting')).css('display','none');
return response;
}, function (response) {
angular.element(document.getElementById('waiting')).css('display','none');
return $q.reject(response);
});
};
}])

Resources