Is it possible to pass event options to a svelte on:* directive?
To register an event listener with options I currently use:
<script lang="ts">
import { onMount } from "svelte"
let root: HTMLElement
onMount(() => {
const touchHandlerOptions = {
passive: true,
}
root.addEventListener("touchstart", handleTouchStart, touchHandlerOptions)
})
</script>
<div bind:this={root} />
Wondering if this could be refactored to pass the options in an on:* directive directly?
There is no way to pass options directly, but you can use event-modifiers:
<div on:touchstart|passive={handleTouchStart}>
See docs
Related
I've set the default OK Button in a Bootstrap-Vue Modal to disabled true and want to change it when inputing something in ab-form-input. Calling the function works but disabling ok-disabled not. Can't get access to the property. Seems to be a very basic question but in the component docs in bootstrap-vue there is only the infor that state can be changed (true-false) but not how to manipulate via script.
`
<template>
<b-modal
id="component-modal"
title="Add Component"
#ok="handleOk"
:ok-disabled="true"
>
<div class="container">
<b-row>
<b-col>Component: </b-col>
<b-col>
<b-form-input
v-model="component"
id="new-component"
required
#input="enableOK"
></b-form-input>
</b-col>
</b-row>
</div>
</b-modal>
</template>
<script>
import axios from 'axios';
import store from '../../store';
export default {
data() {
return {
count: 0,
};
},
methods: {
handleOk() {
this.handleSubmit();
},
handleSubmit() {
this.insertComponentClass(this.component, store.state.project);
delete this.component;
},
insertComponentClass(componentClass, pid) {
const path = `${store.state.apiURL}/componentclass/add`;
const payload = {
name: componentClass,
project_id: pid,
};
axios
.put(path, payload)
.then(() => {
this.$parent.getComponents();
})
.catch((error) => {
console.error(error);
});
},
enableOK() {
console.info('enable ok fired');
this.ok-disable = false; // doesnt wor, linter says "Invalid left-hand side in assignment expression"
},
},
};
</script>
`
There's a few things going on here that are incorrect.
You're binding the ok-disabled prop to a hardcoded value of true in your template. If you want that value to change, you'll need to bind it to a variable that you can update in your components <script>
For example, you can update the modal's :ok-disabled prop to:
:ok-disabled="okDisabled"
Then in your <script> data function, add it to the return object (defaulted to true):
data() {
return {
count: 0,
okDisabled: true,
}
}
Now the modal's :ok-disabled property is bound to that variable and we can change the value in the enableOk method like so:
this.okDisabled = false;
Note regarding the lint error, the name of the variable you're trying to assign to this.ok-disable is not a valid variable name. You can't use a dash (-) character for a Javascript variable name. You can rename it to the property we created earlier this.okDisabled
The generateLegend() wrapper does call the legendCallback defined in my Vue code but I'm lost to how to render the custom HTML in vue-chartjs. What do I do with htmlLegend as described in the vue-chartjs api docs like here.
Here is the line chart component I'm trying to render with a custom HTML object.
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Line,
mixins: [reactiveProp],
props: ['chartData','options'],
data: () => ({
htmlLegend: null
}),
mounted () {
this.renderChart(this.chartData, this.options);
this.htmlLegend = this.generateLegend();
}
}
Here is my vue template
<template>
<div class="col-8">
<line-chart :chart-data="datacollection" :options="chartOptions"></line-chart>
</div>
</template>
Well, htmlLegend holds the markup of the generated legend... so you can just put it into your tag via v-html
<template>
<div class="col-8">
<div class="your-legend" v-html="htmlLegend" />
<line-chart :chart-data="datacollection" :options="chartOptions"></line-chart>
</div>
</template>
mounted() {
this.renderChart( this.chartData , this.options );
var legend = this.generateLegend();
this.$emit('sendLegend', legend)
}
and then in the vue file add a new div to show the legend and also listen to the event to get the legend data
<div class="line-legend" v-html="chartLegend"></div>
<line-chart #sendLegend="setLegend" :chart-data="datacollection" :options="chartOptions"></line-chart>
and also add this to the data
chartLegend: null,
and you also need a method
setLegend (html) {
this.chartLegend = html
},
I want to include an external rtf component in my svelte app.
I tried adding tinymce using the cdn in template.htm and then creating the following svelte component. The editor renders, however I can't get data into or out of the editor.
<script>
import { onMount, tick } from 'svelte'
export let label = ''
export let value = ''
$: console.log('value', value)
onMount(() => {
tinymce.init({
selector: '#tiny',
})
})
</script>
<p>
<label class="w3-text-grey">{label}</label>
<textarea id="tiny" bind:value />
</p>
Super old but encountered this today and found a solution.
Solution:
<svelte:head>
<script src="https://cdn.tiny..."></script>
</svelte:head>
<script>
import {onMount} from 'svelte';
let getHTML;
let myHTML;
onMount(() => {
tinymce.init({
selector: '#tiny'
})
getHTML = () => {
myHTML = tinymce.get('tiny').getContent();
}
})
</script>
<textarea id="tiny" bind:value />
<!-- click to get html from the editor -->
<button on:click={getHTML}>Get HTML from TinyMCE</button>
<!-- html is printed here -->
{myHTML}
Explanation:
My initial thought was to bind per normal with
<textarea bind:value></textarea>
but that doesn't work I think because tinyMCE is doing complicated stuff in the background. Instead of adding the cdn reference in template.htm I used <svelte:head> so it only is loaded for this component. The function tinymce.get('...').getContent() must be called to get the contents of the editor, but it requires tinyMCE, so it must be called within the onMount. So I define a function getHTML within onMount. Now getHTML can be used anywhere to assign the contents of the editor to myHTML.
step one:
run this command on in your terminal
npm install #tinymce/tinymce-svelte
(reference for installation : https://www.tiny.cloud/docs/integrations/svelte/)
step two :
<script>
import { onMount } from 'svelte';
let myComponent;
let summary='';
onMount(async()=>{
const module=await import ('#tinymce/tinymce-svelte');
myComponent=module.default;
})
</script>
step three :
<svelte:component this={myComponent} bind:value={summary}/>
{#html summary}
I am learning how to use Redux. I would like to create a simple application with only one button. When the button is clicked I want to do a rest api call and when the response comes back the response content needs to be displayed.
What I would like to do is send a store.dispatch(CardAction.GET_CARDS) message to Redux when user clicks on the button. I do not want to call rest api directly from the button's onClick handler.
When the answer is received I intend to to the same: send an event with store.dispatch(CardAction.UPDATE_UI) and somehow at the background I want to update the Redux's status.
I hope that this concept is align with React + Redux.
I have some JavaScript code done but some part of it are missing. Could you please help me to put parts together?
index.jsp
<!DOCTYPE html>
<%#page session="false"%>
<%#page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=UTF-8">
<base href="${pageContext.request.contextPath}/" />
<link rel="icon" type="image/x-icon" href="public/image/favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap-theme.min.css">
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
App.js
let store = createStore(reducers);
ReactDom.render(
<Provider store={store}>
<Card/>
</Provider>,
document.getElementById('root')
);
Card.js
export default class Card extends React.Component {
render() {
return (
<div>
...
<Button onClick={() => store.dispatch(CardAction.GET_CARDS)}>rest call</Button>
</div>
)
}
}
ActionType.js
export const GET_CARDS = 'get-cards';
export const UPDATE_UI = 'update-ui';
CardAction.js
export function getCards(param1, param2) {
return createAction(ActionType.GET_CARDS, (param1, param2) => ({ value1, value2 }))
}
export function updateUi() {
return createAction(ActionType.UPDATE_UI)
}
RootReducer.js
export const reducers = (state = {}, action) => {
return action
};
RestClient.js
export default {
cardPost(param1, param2) {
const url = ...;
fetch(url, {
method: 'POST',
credentials: 'include'
})
.then(response => {
if (response.ok) {
console.info('rest response have arrived');
store.dispatch(CardAction.UPDATE_UI)
} else {
console.info('error appeared during calling rest api');
//store.dispatch(CardAction.SHOW_ERROR)
}
})
.catch(function(err) {
console.info(err + ' Url: ' + url)
})
}
}
You should never call store.dispatch() from a component. Instead, you should import a previously built action and let the Redux flow do the remaining stuff. The reducer shouldn't return an action, instead, it should return a new state, without mutating the previous one. I'd suggest you should first compensate some of the comprehensible lack of experience with Redux, and then you can try to follow along with a React-Redux-Rest tutorial like this one: https://medium.com/#rajaraodv/a-guide-for-building-a-react-redux-crud-app-7fe0b8943d0f#.cnat3gbcx
[EDIT]
Here's what I'd do
// component Card.js
import {getCards} from "CardAction";
export default class Card extends React.Component {
render() {
return (
<div>
...
<Button onClick={getCards(param1, param2)}>rest call</Button>
</div>
)
}
}
// action CardAction.js
const receivedCards = (cards) => ({
type: "RECEIVED_CARDS",
cards
})
export function getCards(param1, param2) {
// idk where you're gonna use these params btw
// also please note that fetch() isn't supported by older browsers. Here I'm showing you a simple example with axios, which basically performs the same operation. Feel free to adapt this example code as you want.
return function(dispatch) {
return axios({
url: server + "endpoint",
timeout: 20000,
method: 'get'
})
.then(function(response) {
let cards = response.data;
dispatch(receivedCards(cards));
})
.catch(function(response){
console.log(response.data.error);
})
}
};
// reducer reducer.js
const initialState = {};
export default (state = initialState, action) => {
switch(action.type) {
case "RECEIVED_CARDS":
return Object.assign({},
state,
{cards: action.cards});
default:
return state;
}
}
I'm making an AngularJS app with the facebook sdk. The structure comes as follows:
<html xmlns:fb="http://ogp.me/ns/fb#">
<head>
<title>Reportes para Anunciantes</title>
<meta charset="UTF-8">
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/angular-ui-router.min.js"></script>
<!-- more scripts here -->
<script type="text/javascript" src="js/index.js"></script>
</head>
<body>
<script type="text/javascript">
window.fbAsyncInit = function() {
FB.init({
appId : 'MYAPPID', //Have an actual app id in my source
cookie : true, // enable cookies to allow the server to access
// the session
xfbml : true, // parse social plugins on this page
version : 'v2.0' // use version 2.0
});
angular.bootstrap(document, ['Application']);
};
// Load the SDK asynchronously
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/es_LA/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<div id="content" ui-view>
Cargando API de Facebook ...
</div>
</body>
</html>
In the page, I load Angular once the Facebook API is loaded. The site uses xfbml for the login buttons.
The whole angular app is in index.js:
var Application = angular.module(
"Application",
['ui.router'],
['$stateProvider', function($stateProvider){
//$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
$stateProvider
.state('notloggedin', {
templateUrl: 'templates/notloggedin.html',
controller: 'Application.NotLoggedIn'
})
.state('notauthorized', {
templateUrl: 'templates/notauthorized.html',
controller: 'Application.NotAuthorized'
})
.state('inapp', {
templateUrl: 'templates/inapp.html',
controller: 'Application.InApp'
});
}]
);
function FBStatusChecker($state){
this._state = $state;
}
FBStatusChecker.prototype = {
checkAndGo: function(response) { /* omitted - no issue here */ },
/* omitted methods - no issue here */
updateState: function(){
var context = this;
FB.getLoginStatus(function(response) {
context.checkAndGo(response);
})
}
};
Application.StatusChangeService = Application.service('Application.FBStatusChecker', ['$state', FBStatusChecker]);
Application.directive("fbLoginButton", function() {
return {
restrict: 'E',
link: function (scope, iElement, iAttrs) {
console.log("your ass");
if (FB) {
console.log("is broken");
FB.XFBML.parse(iElement[0]);
}
}
}
});
/* omitted controllers - no issue here.
two of them are empty, and one correctly updates the scope
via $scope.$apply(...) for an asynchronous callback */
Application.run(['Application.FBStatusChecker', function(statusChecker){
statusChecker.updateState();
}]);
Let's analyze by parts:
The page uses ui-router to switch between application states. This works as expected. Angular is only loaded (i.e. my module being run) once the FB API was successfully loaded.
The application defines three states, and in run() one of those states is selected.
I omitted the controllers, since two are empty, and the third updates -with no issues- the scope.
I omitted the state-switcher service methods (this has the task to use $state to switch to one of three states depending on facebook state).
There's a directive named fbLoginButton. This directive must be recognized as 'E' (lement), and should match fb:login-button. The directive is successfully matched.
I had no issue generating the button before using AngularJS and ui-router.
Finally, the templates come:
notloggedin.html
<fb:login-button max_rows="1" size="xlarge" show_faces="false" auto_logout_link="false"></fb:login-button>
Debes iniciar sesiĆ³n en Facebook para continuar.
notauthorized.html
<fb:login-button max_rows="1" size="xlarge" show_faces="false" auto_logout_link="false"></fb:login-button>
Debes autorizar la aplicaciĆ³n para continuar.
inapp.html
<span ng-if="name">
Bienvenido {{ name }}!
</span>
<span ng-if="!name">
Listando ...
</span>
Notes: Also tried HTML5 solution, using div, instead of xfbml.
And my issue is: The facebook buttons are not being shown. Only the corresponding texts are being shown. If i'm logged in Facebook when I hit this page, I get correctly redirected to the expected state, with the {{ name }} being fetched. The same occurs with the other states, so I'm not having issue specificly with graph api, but with xfbml.
And my question is: What am I missing? The directive is being matched but seems that FM.XML.parse has no effect. How can I fix it?
Apparently -discovered it by a lucky mistake- the .parse method ignores the current element, so the parse must start from the parent element. So far, the directive must be changed to:
Application.directive("fbLoginButton", function() {
return {
restrict: 'E',
link: function (scope, iElement, iAttrs) {
if (FB) {
FB.XFBML.parse(iElement[0].parent);
}
}
}
});
which consisted in just adding the .parent property to get the parent DOM node.
However, if two items matched by this directive are siblings (or using another directive with the same functionality), this could be run twice. So recording the element in an array after parsing it, and checking before parsing it, would be a good workaround to avoid doing the parsing twice. Another alternative would be to force a parsing in each state's controller, to the ui-view-holding container. The latter would be implemented like this:
The only directive will be an attribute-based directive, e.g. xfbml-parse in the ui-view element (in my case):
Application.directive("fbLoginButton", function() {
return {
restrict: 'E',
link: function (scope, iElement, iAttrs) {
if (FB) {
FB.XFBML.parse(iElement[0].parent);
}
}
}
});
being no need to implement other directives.
After that, use xfbml-parse directive in the ui-view element. In my case:
<div id="content" ui-view xfbml-parse>
Cargando API de Facebook ...
</div>
which will run the link function every time the state changes.
So: every time the ui-view node renders, the XFBML will be parsed.