I have around four components using the same mixin that I created. In order to get the dimensions of this.$el (the CORRECT ones), I had to implement window.onload = function() mixin. My mixin looks like this:
module.exports = {
methods: {
onPageload: function( callback ) {
window.onload = function() {
callback();
}
}
}
};
Very simple. However, more than one component uses it. I'm creating a slideshow. My Slides component uses it, my Slide component, my Thumb component, and my overall Slider component.
HOWEVER only the very last one to compile fires it.
var pageLoad = require('../../mixins/Pageload');
module.exports = {
template: require('./templates/thumbs.html'),
replace: true,
data: function() {
return {
style: {
width: 800
},
count: 2
}
},
computed: {
styles: function() {
return {
width: this.style.width + 'px'
}
}
},
mixins: [pageLoad],
props: ['count'],
attached: function() {
this.onPageload( this.setDimensions );
},
methods: {
// Set dimensions for the first time
setDimensions: function() {
console.log('setting thumb');
this.style.width = this.$parent.slideWidth;
}
}
};
My other "parent" components do the same thing to set their dimensions. This is the MOST inner child of the entire thing.. it's the ONLY one that fires. If I erase it from here, the next child up is the only one that fires. They are overwriting each other in a way. My vue instance is here:
new Vue({
el: '#slideshow',
components: {
'sliderarrows': require('../../components/slider/SliderArrows'),
'sliderthumb': require('../../components/slider/SliderThumb'),
'sliderslide': require('../../components/slider/SliderSlide'),
'slides': require('../../components/slider/SliderSlides'),
'slider': require('../../components/slider/Slider'),
'thumbnails': require('../../components/slider/SliderThumbs')
}
});
So the thumbnails component is the only one that's firing the mixin method. I feel like it's something to do with the whole compilation of require() but I'm not sure since I'm not that familiar with the Node.js module format.
Thanks for any help on how to get this to work!
I had to change my mixin to this:
module.exports = {
methods: {
onPageload: function( callback ) {
window.onload = callback();
}
}
};
It was something with the window.onload opening a new function.
Related
I'm trying to use Esri map. To include map in my project, here is what I found:
require([
"esri/map",
"esri/dijit/Search",
"esri/dijit/LocateButton",
"esri/geometry/Point",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
But there isn't any esri folder or npm package. Therefore, I'm confused here. How esri is imported in project?
Use esri-loader to load the required esri modules. This is a component rendering basemap.
import React, { Component } from 'react';
import { loadModules } from 'esri-loader';
const options = {
url: 'https://js.arcgis.com/4.6/'
};
const styles = {
container: {
height: '100vh',
width: '100vw'
},
mapDiv: {
padding: 0,
margin: 0,
height: '100%',
width: '100%'
},
}
class BaseMap extends Component {
constructor(props) {
super(props);
this.state = {
status: 'loading'
}
}
componentDidMount() {
loadModules(['esri/Map', 'esri/views/MapView'], options)
.then(([Map, MapView]) => {
const map = new Map({ basemap: "streets" });
const view = new MapView({
container: "viewDiv",
map,
zoom: 15,
center: [78.4867, 17.3850]
});
view.then(() => {
this.setState({
map,
view,
status: 'loaded'
});
});
})
}
renderMap() {
if(this.state.status === 'loading') {
return <div>loading</div>;
}
}
render() {
return(
<div style={styles.container}>
<div id='viewDiv' style={ styles.mapDiv } >
{this.renderMap()}
</div>
</div>
)
}
}
export default BaseMap;
This renders a base map but this is not responsive. If I remove the div around the view div or if I give the height and width of the outer div (surrounding viewDiv) as relative ({ height: '100%', width: '100%'}), the map does not render. No idea why. Any suggestions to make it responsive would be appreciated.
An alternative method to the above is the one demonstrated in esri-react-router-example. That application uses a library called esri-loader to lazy load the ArcGIS API only in components/routes where it is needed. Example:
First, install the esri-loader libary:
npm install esri-loader --save
Then import the esri-loader functions in any react module:
import * as esriLoader from 'esri-loader'
Then lazy load the ArcGIS API:
componentDidMount () {
if (!esriLoader.isLoaded()) {
// lazy load the arcgis api
const options = {
// use a specific version instead of latest 4.x
url: '//js.arcgis.com/3.18compact/'
}
esriLoader.bootstrap((err) => {
if (err) {
console.error(err)
}
// now that the arcgis api has loaded, we can create the map
this._createMap()
}, options)
} else {
// arcgis api is already loaded, just create the map
this._createMap()
}
},
Then load and the ArcGIS API's (Dojo) modules that are needed to create a map:
_createMap () {
// get item id from route params or use default
const itemId = this.props.params.itemId || '8e42e164d4174da09f61fe0d3f206641'
// require the map class
esriLoader.dojoRequire(['esri/arcgis/utils'], (arcgisUtils) => {
// create a map at a DOM node in this component
arcgisUtils.createMap(itemId, this.refs.map)
.then((response) => {
// hide the loading indicator
// and show the map title
// NOTE: this will trigger a rerender
this.setState({
mapLoaded: true,
item: response.itemInfo.item
})
})
})
}
The benefit of using esri-loader over the approach shown above is that you don't have to use the Dojo loader and toolchain to load and build your entire application. You can use the React toolchain of your choice (webpack, etc).
This blog post explains how this approach works and compares it to other (similar) approaches used in applications like esri-redux.
You don't need to import esri api like you do for ReactJS. As the react file will finally compile into a js file you need to write the esri parts as it is and mix the ReactJS part for handling the dom node, which is the main purpose of ReactJS.
A sample from the links below is here
define([
'react',
'esri/toolbars/draw',
'esri/geometry/geometryEngine',
'dojo/topic',
'dojo/on',
'helpers/NumFormatter'
], function(
React,
Draw, geomEngine,
topic, on,
format
) {
var fixed = format(3);
var DrawToolWidget = React.createClass({
getInitialState: function() {
return {
startPoint: null,
btnText: 'Draw Line',
distance: 0,
x: 0,
y: 0
};
},
componentDidMount: function() {
this.draw = new Draw(this.props.map);
this.handler = this.draw.on('draw-end', this.onDrawEnd);
this.subscriber = topic.subscribe(
'map-mouse-move', this.mapCoordsUpdate
);
},
componentWillUnMount: function() {
this.handler.remove();
this.subscriber.remove();
},
onDrawEnd: function(e) {
this.draw.deactivate();
this.setState({
startPoint: null,
btnText: 'Draw Line'
});
},
mapCoordsUpdate: function(data) {
this.setState(data);
// not sure I like this conditional check
if (this.state.startPoint) {
this.updateDistance(data);
}
},
updateDistance: function(endPoint) {
var distance = geomEngine.distance(this.state.startPoint, endPoint);
this.setState({ distance: distance });
},
drawLine: function() {
this.setState({ btnText: 'Drawing...' });
this.draw.activate(Draw.POLYLINE);
on.once(this.props.map, 'click', function(e) {
this.setState({ startPoint: e.mapPoint });
// soo hacky, but Draw.LINE interaction is odd to use
on.once(this.props.map, 'click', function() {
this.onDrawEnd();
}.bind(this));
}.bind(this))
},
render: function() {
return (
<div className='well'>
<button className='btn btn-primary' onClick={this.drawLine}>
{this.state.btnText}
</button>
<hr />
<p>
<label>Distance: {fixed(this.state.distance)}</label>
</p>
</div>
);
}
});
return DrawToolWidget;
});
Below are the links where you can find information in detail.
http://odoe.net/blog/esrijs-reactjs/
https://geonet.esri.com/people/odoe/blog/2015/04/01/esrijs-with-reactjs-updated
I want to override the existing magento2 JS Component in my theme for some more customization.
Magento_Checkout/js/view/minicart.js
Above JS component i want to override and i want to add some more operation on the remove button event.
You can try "map" of require js. I used this and working for me. following is the requirejs-config.js inside my theme.
var config = {
map: {
'*': {
'Magento_Checkout/js/view/minicart':'js/custom/minicart'
}
}
};
Modified minicart.js file is placed inside "web/js/custom" folder inside my theme.
Just Go to your theme Override Magento_Checkout there, then under web folder make path as same as core module then add your js file & do required changes. It will reflect on frontend.
You can also extend an existing Magento JS without overwriting the whole file in your module add the require-config.js
app/code/MyVendor/MyModule/view/frontend/requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/view/minicart': {
'MyVendor_MyModule/js/minicart': true
}
}
}
};
Then add the minicart.js
app/code/MyVendor/MyModule/view/frontend/web/js/minicart.js
define([], function () {
'use strict';
return function (Component) {
return Component.extend({
/**
* #override
*/
initialize: function () {
var self = this;
return this._super();
},
MyCustomFunction: function () {
return "my function";
}
});
}
});
define(['jquery'],function ($) {
'use strict';
var mixin = {
/**
*
* #param {Column} elem
*/
initSidebar: function () {
var sidebarInitialized = false, miniCart;
miniCart = $('[data-block=\'minicart\']');
if (miniCart.data('mageSidebar')) {
miniCart.sidebar('update');
}
if (!$('[data-role=product-item]').length) {
return false;
}
miniCart.trigger('contentUpdated');
if (sidebarInitialized) {
return false;
}
sidebarInitialized = true;
miniCart.sidebar({
'targetElement': 'div.block.block-minicart',
'url': {
'checkout': window.checkout.checkoutUrl,
'update': window.checkout.updateItemQtyUrl,
'remove': window.checkout.removeItemUrl,
'loginUrl': window.checkout.customerLoginUrl,
'isRedirectRequired': window.checkout.isRedirectRequired
},
'button': {
'checkout': '#top-cart-btn-checkout',
'remove': '#mini-cart a.action.delete',
'increacseqty':'#mini-cart a.action.increase-qty',
'decreaseqty':'#mini-cart a.action.decrease-qty',
'close': '#btn-minicart-close'
},
'showcart': {
'parent': 'span.counter',
'qty': 'span.counter-number',
'label': 'span.counter-label'
},
'minicart': {
'list': '#mini-cart',
'content': '#minicart-content-wrapper',
'qty': 'div.items-total',
'subtotal': 'div.subtotal span.price',
'maxItemsVisible': window.checkout.minicartMaxItemsVisible
},
'item': {
'qty': ':input.cart-item-qty',
'button': ':button.update-cart-item'
},
'confirmMessage': $.mage.__('Are you sure you would like to remove this item from the shopping cart??')
});
return this._super();
}
};
return function (minicart) { // target == Result that Magento_Ui/.../columns returns.
return minicart.extend(mixin); // new result that all other modules receive
};
});
My front-end page is made by React + Flux, which sends the script data to back-end nodejs server.
The script data is an Array which contains the linux shell arguments (more than 100000). When to back-end received, it will execute the linux shell command.
Just an example:
cat ~/testfile1
cat ~/testfile2
.
.
.
(100000 times ...etc)
When the backend finished one of the linux shell commands, I can save the readed content to result data. Therefore, socket.io will emit the result data to the front-end.
I want to get the result data from my webpage in real time, so I have done some stuff in my project below.
My React component code:
import React from 'react';
import AppActions from '../../../actions/app-actions';
import SocketStore from '../../../stores/socket-store';
import ResultStore from '../../../stores/result-store';
function getSocket () {
return SocketStore.getSocket();
}
function getResult () {
return ResultStore.getResultItem();
}
class ListResultItem extends React.Component {
constructor () {
super();
}
render () {
return <li>
{this.props.result.get('name')} {this.props.result.get('txt')}
</li>;
}
}
class ShowResult extends React.Component {
constructor () {
super();
this.state = {
socket: getSocket(),
result: getResult()
};
}
componentWillMount () {
ResultStore.addChangeListener(this._onChange.bind(this));
}
_onChange () {
this.setState({
result: getResult()
});
}
render () {
return <div>
<ol>
{this.state.result.map(function(item, index) {
return <ListResultItem key={index} result={item} />;
})}
</ol>
</div>;
}
componentDidMount () {
this.state.socket.on('result', function (data) {
AppActions.addResult(data);
});
}
}
My Flux store (ResultStore) code:
import AppConstants from '../constants/app-constants.js';
import { dispatch, register } from '../dispatchers/app-dispatcher.js';
import { EventEmitter } from 'events';
import Immutable from 'immutable';
const CHANGE_EVENT = 'changeResult';
let _resultItem = Immutable.List();
const _addResult = (result) => {
let immObj = Immutable.fromJS(result);
_resultItem = _resultItem.push(immObj);
}
const _clearResult = () => {
_resultItem = _resultItem.clear();
}
const ResultStore = Object.assign(EventEmitter.prototype, {
emitChange (){
this.emit( CHANGE_EVENT );
},
addChangeListener (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener (callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getResultItem () {
return _resultItem;
},
dispatcherIndex: register(function (action) {
switch (action.actionType) {
case AppConstants.ADD_RESULT:
_addResult(action.result);
break;
case AppConstants.CLEAR_RESULT:
_clearResult();
break;
}
ResultStore.emitChange();
})
});
However, the page will become very slow after rendering more than 1000 datas. How to enhance the performance for rendering? I need to execute the linux script persistently more than 3 days. Any solutions? Thanks~
Is there any need to render all the data on screen? If not then there are a few ways to deal with cutting down the amount of visible data.
Filter / Search
You can provide a search/filter component that complements the list and creates a predicate function that can be used to determine whether each item should or should not be rendered.
<PredicateList>
<Search />
<Filter />
{this.state.result
.filter(predicate)
.map(function(item, index) {
return <ListResultItem key={index} result={item} />;
})
}
</PredicateList>
Lazy Load
Load the items only when they are asked for. You can work out whether item is needed by keeping track of whether it would be onscreen, or whether the mouse was over it.
var Lazy = React.createClass({
getInitialState: function() {
return { loaded: false };
},
load: function() {
this.setState({ loaded: true });
},
render: function() {
var loaded = this.state.loaded,
component = this.props.children,
lazyContainer = <div onMouseEnter={this.load} />;
return loaded ?
component
lazyContainer;
}
});
Then simply wrap your data items inside these Lazy wrappers to have them render when they are requested.
<Lazy>
<ListResultItem key={index} result={item} />
</Lazy>
This ensures that only data needed by the user is seen. You could also improve the load trigger to work for more complex scenarios, such as when the component has been onscreen for more then 2 seconds.
Pagination
Finally, the last and most tried and tested approach is pagination. Choose a limit for a number of data items that can be shown in one go, then allow users to navigate through the data set in chunks.
var Paginate = React.createClass({
getDefaultProps: function() {
return { items: [], perPage: 100 };
},
getInitialState: function() {
return { page: 0 };
},
next: function() {
this.setState({ page: this.state.page + 1});
},
prev: function() {
this.setState({ page: this.state.page - 1});
},
render: function() {
var perPage = this.props.perPage,
currentPage = this.state.page,
itemCount = this.props.items.length;
var start = currentPage * perPage,
end = Math.min(itemCount, start + perPage);
var selectedItems = this.props.items.slice(start, end);
return (
<div className='pagination'>
{selectedItems.map(function(item, index) {
<ListResultItem key={index} result={item} />
})}
<a onClick={this.prev}>Previous {{this.state.perPage}} items</a>
<a onClick={this.next}>Next {{this.state.perPage}} items</a>
</div>
);
}
});
These are just very rough examples of implementations for managing the rendering of large amounts of data in efficient ways, but hopefully they will make enough sense for you to implement your own solution.
I have a little problem with my code and need some advice.
I try to simulate a diceroll with Vue.js. To be sure any diceroll is different, i want to create a component for that. I use that code for my app.js
Vue.component('diceroll', {
template: 'This is the result !' + diceroll,
data: function() {
return {
diceroll: 0
}
},
methods: function(){
diceroll: Math.floor(Math.random() * 6) + 1;
}
}
)
var demo = new Vue( {
el: ' #demo',
}
)
Obviously, it don't work and i don't understand how to do that. I read the doc and watch the laracast's series but...
Someone can help me on this ? ^^
"methods" in Vue are actually objects (key-value pair) where the value is a function. Also, inside the template you have to refer variables using mustache binding like this: {{ vName }}.
I made example: (here is a jsbin demo)
Vue.component('diceroll', {
template: 'This is the result: {{diceroll}}',
data: function() {
return {
diceroll: 0
};
},
methods: {
roll: function() {
this.diceroll = Math.floor(Math.random() * 6) + 1;
}
},
ready: function() {
this.roll();
}
});
var demo = new Vue({
el: '#demo'
});
<script src="http://vuejs.org/js/vue.js"></script>
<div id="demo">
<diceroll></diceroll>
</div>
In my app I have layouts and views for those layouts. Layouts only change on login/logout, but the other special case is on pageload I need to load the proper layout. However, in my defaultAction my layout does not actually render after it returns and so when the view tries to render, the el it is supposed to be contained in does not exist.
// Filename: router.js
var app_router;
define( [ 'views/layouts/beta', 'views/beta/requestInvite', 'views/beta/login', 'views/app/dashboard' ],
function(betaLayout, requestInviteView, loginView, dashboardView) {
var AppRouter = Backbone.Router.extend( {
routes : {
// Pages
'login' : 'login',
'dashboard' : 'dashboard',
// Default
'*actions' : 'defaultAction'
},
// Pages
login : function() {
loginView.render();
},
dashboard : function() {
dashboardView.render();
},
// Default
defaultAction : function(actions) {
betaLayout.render();
requestInviteView.render();
}
});
var initialize = function() {
app_router = new AppRouter;
$('a').live('click', function() {
var href = $(this).attr('href');
// only navigate to real links
if(href == undefined)
return;
app_router.navigate(href, {trigger: true});
return false;
});
Backbone.history.start({pushState: true});
};
return {
initialize : initialize
};
});
How can I have my layout render completely before my view?
Define a callback for betaLayout.render() to take as an argument, that gets executed when the render is actually complete.
E.g., betaLayout.render() would look something like:
render: function(callback) {
/* code to render your element,
using the following line when all
asynchronous calls are complete */
if (callback) callback();
}
and your defaultAction would use it like so, passing the second render() as its callback.
betaLayout.render(requestInviteView.render);
The problem was that because my layout didn't render before my view was initialized, the el was empty. What I did was convert all of my objects to return the object instead of the instance, and let them render once they were initialized. This way, when I declare a new MyLayout and then a new MyView, I can be guaranteed that MyView's el is valid.