I'm rendering out components that have properties with liquid strings. These components are being rendered on the server and picked back up again in the client. Essentially I'm using the DOM as a data store. I'm debating on methods of where to store the data. I need the component to render out valid markup to the server for SEO. But I don't need to pick back up the variable like I am here with this.refs.variantId.getDOMNode(). I could for instance set the variantId to a global client side javascript variable somewhere higher then this code in essence something like var variantId = "{{ product.variants[0].id }}";.
This component will render to a string and be placed within a template file on a server, the server will process that HTML come across the {{ product.variants[0].id }} variable and turn it into something like 1058477584. My component needs to reach into the existing DOM for itself and pull the value out.
var React = require("react");
var $ = require("jquery");
module.exports = React.createClass({
handleSubmit: function(e){
e.preventDefault();
var variantId = this.refs.variantId.getDOMNode().value.trim();
$.ajax({
url: "/cart/add.js",
method: "post",
dataType: "json",
data: {
"id": variantId,
"quantity": this.props.quantity,
},
success: function(data) {
// emit cart added event
}.bind(this),
error: function(xhr, status, err) {
// emit error event (cart added)
}.bind(this)
});
},
getDefaultProps: function(){
return {
quantity: 1,
variantId: "{{ product.variants[0].id }}",
buttonText: "Add to cart"
}
},
render: function() {
return (
<div className="buyButton">
<form action="/cart/add" method="post" encType="multipart/form-data" onSubmit={this.handleSubmit}>
<input type="hidden" name="quantity" value={ this.props.quantity } />
<input type="hidden" name="id" ref="variantId" value={ this.props.variantId } />
<button type="submit" className="btn btn-holstee">{this.props.buttonText}</button>
</form>
</div>
);
}
});
I'm wondering what people think about rendering components with another templating language as a string property. Does it make sense to store that data anywhere else? I don't have access to a server that can store individual pages. It's all templates so multiple data sources need to be handled by one route.
Is there a better way to abstract the liquid out of the component?
Is there a better way to call DOMNodes / update all the props to DOMNodes?
I do something similar in my app. I serialize with JSON and put it in a script tag in the DOM
<script type="application/json" id="preload-notifications">{{json_encode($preload_notifications)}}</script>
Then,
var raw = document.getElementById('preload-messaging');
if (raw === null) {
return ;
}
var data = JSON.parse(raw.text);
I use the flux architecture so it's really simple
this.dispatch('messaging', {
messages: data
});
But you could can inject it as a prop.
React.render(<MessagingContainer messages={data} />, messageDomNode);
Whatever you do, I suggest you don't query the DOM inside a React component. Try to pass stuff as props as much as possible.
Related
I'm trying to display some html entities in a form text input, but v-model seems escaping them.
Is there something I need to write to make v-model displaying correctly html entities?
my sample code is
<el-input v-model="data" readonly="readonly"></el-input>
I know about v-html but I prefer keep using v-model due the automatic two-way binding.
UPDATE
Maybe I expressed myself wrong, I want to display the character, not the html entity, so instead 49.42₹ i need to display 49.42₹.
If you v-model a computed that interprets HTML entities, I think you get the effect you want. You can type in entity values and it will interpret them correctly. However, it might prematurely turn  into a different character; you have to type #8377; and then go back in and insert the &.
new Vue({
el: '#app',
data: {
a: '49.42₹'
},
computed: {
asText: {
get() {
return this.toText(this.a);
},
set(newValue) {
this.a = newValue;
}
}
},
methods: {
toText(html) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent;
}
}
})
<link href="//unpkg.com/element-ui#1.0.0-rc.3/lib/theme-default/index.css" rel="stylesheet"/>
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<el-input v-model="asText"></el-input>
{{a}}
<div v-html="a"></div>
</div>
as I'm on my Vue spree (started recently but so far I'm really enjoying learning this framework) couple of questions rised up. One of which is how to post form from multiple components. So before I continue forward I wanted to ask you what are you thinking about this way of structuring and point me in right direction if I'm wrong.
Here it goes.
I'm working on a SPA project using ASP.NET CORE 2.1 and Vue JS Template (with webpack)(https://github.com/MarkPieszak/aspnetcore-Vue-starter) and my project is structured in several containers, something like this:
In my app-root i registered several containers
<template>
<div id="app" class="container">
<app-first-container></app-first-container>
<app-second-container></app-second-container>
<!--<app-third-container></app-third-container>-->
<app-calculate-container></app-calculate-container>
<app-result-container></app-result-container>
</div>
</template>
<script>
// imported templates
import firstContainer from './first-container'
import secondContainer from './second-container'
import calculateContainer from './calculateButton-container'
//import thirdContainer from './third-container'
import resultContainer from './result-container'
export default {
components: {
'app-first-container': firstContainer,
'app-second-container': secondContainer,
// 'app-third-container': thirdContainer,
'app-calculate-container': calculateContainer,
'app-result-container': resultContainer
}
}
</script>
In my first container I'm having several dropdowns and two input fields with my script file where I'm fetching data from API and filling dropdowns and input fields with fetched data.
Something like this ( entered some dummy code for demonstration)
<template>
<div>
<h1>Crops table</h1>
<p>This component demonstrates fetching data from the server. {{dataMessage}}</p>
<div class="form-row">
<div class="form-group col-md-6">
<label for="exampleFormControlSelect1" class="col-form-label-sm font-weight-bold">1. Some text</label>
<select class="form-control" id="exampleFormControlSelect1" v-model="pickedCropType" #change="getCropsByType()">
<option v-for="(cropType, index) in cropTypes" :key="index" :value="cropType.id" :data-imagesrc="cropType.imgPath">{{ cropType.name }}</option>
</select>
</div>
<div class="form-group col-md-6">
<label for="exampleFormControlSelect2" class="col-form-label-sm font-weight-bold">2. Some text</label>
<select class="form-control" id="exampleFormControlSelect2">
<option v-for="(crop, index) in cropSelectList" :key="index" :value="crop.id">{{ crop.name }}</option>
</select>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex'
export default {
data() {
return {
cropTypes: null,
cropSelectList: null,
crops: null,
pickedCropType: null,
}
},
methods: {
loadPage: async function () {
try {
//Get crop types and create a new array with crop types with an added imgPath property
var cropTypesFinal = [];
let responseCropTypes = await this.$http.get(`http://localhost:8006/api/someData`);
responseCropTypes.data.data.forEach(function (element) {
cropTypesFinal.push(tmpType);
});
} catch (err) {
window.alert(err)
console.log(err)
}
},
getCropsByType: async function () {
//Get crops by crop type
let responseCrops = await this.$http.get(`http://localhost:8006/api/crop/Type/${this.pickedCropType}`);
var responseCropsData = responseCrops.data.data;
this.cropSelectList = responseCropsData;
}
},
async created() {
this.loadPage()
}
}
</script>
And in my second container I have different dropdowns and different input fields with different scripts etc.
So, my questions are:
1.) I'm having required data form field in first container and in second container I'm having additional data and my submit button is separated in third container (app-result-container). So, is this proper and logical way of structuring containers if not can you point me in right direction?
2.) Is it smart to input script tag in every container where I'm processing/fetching/submitting some data for that particular container? Should I put scripts tag in separated file and keep structure clean, separating html from js file.
Example:
import { something } from 'something'
export default {
data () {
return {
someData: 'Hello'
}
},
methods: {
consoleLogData: function (event) {
Console.log(this.someData)
}
}
}
3.) Can I send input values from one container to another (In my particular case from first and second container to app-calculate-container(third container))?
How to on submit return results container with calculated imported values
If you want components to communicate or share data with one another, you will need to either emit an event from one component up to the parent and pass it down via props, or use some kind of state management model, like Vuex, where each of your components can listen to the store.
Take a look at this code sandbox: https://codesandbox.io/s/8144oy7xy2
App.vue
<template>
<div id="app">
<child-input #input="updateName" />
<child-output :value="name" />
</div>
</template>
<script>
import ChildInput from "#/components/ChildInput.vue";
import ChildOutput from "#/components/ChildOutput.vue";
export default {
name: "App",
components: {
ChildInput,
ChildOutput
},
data() {
return {
name: ""
};
},
methods: {
updateName(e) {
this.name = e.target.value;
}
}
};
</script>
ChildInput.vue
<template>
<input type="text" #input="changeHandler">
</template>
<script>
export default {
name: "ChildInput",
methods: {
changeHandler(e) {
this.$emit("input", e);
}
}
};
</script>
ChildOutput.vue
<template>
<p>{{ value }}</p>
</template>
<script>
export default {
name: "ChildOutput",
props: {
value: {
type: String,
default: ""
}
}
};
</script>
What's going on?
The ChildInput component is a text field and on every change inside it, fires an event (emits using this.$emit() and passes the whole event up).
When this fires, App is listening to the change, which fires a method that updates the name data property.
Because name is a reactive data property and is being passed down as a prop to the ChildOutput component, the screen re-renders and is updated with the text written.
Neither ChildInput nor ChildOutput knows about one another. It's the parent that listens to the event passed to it, then passes the new prop down.
This way of working is fine and simple to understand, but I would strongly recommend looking at Vuex, as this method can get messy and complicated when you go beyond trivial tasks.
I'm trying to pass a custom form attribute (category) through jQuery UI Autocomplete to use in a product search. The form looks like <form id="BulkOrderForm" category="samplecategory"><input></input>...</form> and contains inputs that use the autocomplete script. There can be several forms on each page, so I need to be able to get the category value from the form that contains the active input field.
Here's my source:
function autocomplete() {
$("input.wcbulkorderproduct").autocomplete({
element: function(){
var element = $('form#BulkOrderForm').attr('category');
return element;
},
source: function(request, response, element){
$.ajax({
url: WCBulkOrder.url+'?callback=?&action='+acs_action+'&_wpnonce='+WCBulkOrder.search_products_nonce,
dataType: "json",
data: {
term: request.term,
category: element
},
success: function(data) {
response(data);
}
});
}
});
}
Any thoughts on how this can be acheived?
If I'm understanding correctly, you're trying to use the active input's parent form in the ajax request. Here's a way to achieve that:
Html:
<form data-category="form1">
<input type="text" />
<input type="text" />
</form>
<form data-category="formB">
<input type="text" />
<input type="text" />
</form>
JS:
$('form').each(function () {
var category = $(this).data('category');
$(this).find('input').autocomplete({
source: function (request, response) {
response([category]);
}
});
});
Instead of using autocomplete on a catch-all selector that gets inputs from all forms, first select the forms themselves. For each one, extract the category, then select all child inputs and call autocomplete on that result. Then you can use the category variable in the ajax call - in the example I'm simply passing it to the callback to display.
http://jsfiddle.net/Fw2QA/
I'll give you another solution, you can lookup the parent form of the active input, and extract the attribute from it. Because I don't know if this category in your form is dynamic or no, or either if you can control all of the process involved in your code, I'll give you a more generic solution, although if that attribute is dynamic "Turch" solution is way better than mine, by letting the data functionality of jquery handle the attribute changes, if it's static, than you can just do it like this:
function autocomplete() {
var element = $("input.wcbulkorderproduct").autocomplete({
source: function(request, response){
$.ajax({
url: WCBulkOrder.url+'?callback=?&action='+acs_action+'&_wpnonce='+WCBulkOrder.search_products_nonce,
dataType: "json",
data: {
term: request.term,
category: element
},
success: function(data) {
response(data);
}
});
}
}).parents('form').first().attr('category');
//chained call, sets autocomplete, grabs the parent form and the attribute
//which is saved on the variable element, and is used on every call through
//javascript context inheritance.
}
UPDATE
A little example illustrating my suggestion (provided by #Turch > thanks), can be found here.
I'm doing something wrong. I'm attempting to get the stored value I have in goinstant. I have a person room with a userName. The value the alert function displays is "[object Object]". Here is my code: (I left out the scripts intentionally). I provided a quick screen shot of my person data on goInstant for reference http://screencast.com/t/BtLqfrorg
<h2>Angular JS Test</h2>
<div ng-app="testapp" data-ng-controller="personCtrl">
<input type="text" ng-model="userName" />{{ userName }}
<button type="submit" id="save" name="save" >Save</button>
<script>
var testApp = angular.module('testapp', ['goangular']);
testApp.config(function($goConnectionProvider) {
$goConnectionProvider.$set('https://goinstant.net/<mykey>/test');
});
testApp.controller('personCtrl', function($scope, $goKey) {
// $goKey is available
$scope.userName = $goKey('/person/userName').$sync();
alert($scope.userName);
});
</script>
</div>
Your example would indicate that you expect $scope.userName to be a primitive value (a string). It is in fact, a model. Models provide a simple interface for updating the state of your application, and in GoAngular, that state is persisted to your GoInstant App auto-magically.
You can find more documentation on the GoAngular Model here. I thought a working example might help, so I've created a Plunker. Let's work through the script.js:
angular
.module('TestThings', ['goangular'])
.config(function($goConnectionProvider) {
$goConnectionProvider.$set('https://goinstant.net/mattcreager/DingDong');
})
.controller('TestCtrl', function($scope, $goKey) {
// Create a person model
$scope.person = $goKey('person').$sync();
// Observe the model for changes
$scope.$watchCollection('person', function(a, b) {
console.log('model is', a.$omit()); // Log current state of person
console.log('model was', b.$omit()); // Log the previous state of person
});
// After 2000 ms set the userName property of the person model
setTimeout(function() {
$scope.person.$key('userName').$set('Luke Skywalker');
}, 2000);
// Set the userName property of the person model
$scope.person.$key('userName').$set('Darth Vader');
});
I have a table of items.
The first column of each row contains a checkbox input as part of a form.
The user can click on these checkboxes and then click submit to do bulk actions such as delete items.
I have setup (using jquery) a situation whereby on clicking the 'Add data' link in a row column, a DIFFERENT form is loaded into a third column in which a user can enter item data.
What i then want to do is use ajax to submit this second form. To do this I am using the following code:
$(document).on('submit',".add_form",function(event)
{
event.preventDefault();
var serial=$(this).serialize();
var domain=$('[name=domain]').val();
$.ajax({
url:"portfolio/transactions/"+domain+"/",
type:"post",
data: {data:serial},
success: function(dat){
$('#transactions_div').html(dat);
}
});
});
This however does NOT work, and I believe this is because html does not allow forms within forms. My assumption is that jQuery follows such standards and is getting confused when a second form is loaded into a div which is contained within another set of tags.
Given this, is what I want to do simply not possible?
THanks
Try this:
$(document).on('submit', ".add_form", function (event) {
event.preventDefault();
var serial = $(this).serializeArray();
var domain = $('[name=domain]').val();
$.ajax({
url: "portfolio/transactions/" + domain + "/",
type: "POST",
data: serial,
success: function (data) {
$('#transactions_div').html(data);
}
});
});
HTML
<form class="add_form">
<form class="secondForm">
<input type="checkbox" />
</form>
<input name="domain" type="text" />
<!-- Replace the submit-Button with a normal button -->
<input type="button" value="Send" id="send"/>
</form>
JS
$(document).on('click', "#send", function () {
var serial = $(".add_form").serialize();
var domain = $('[name=domain]').val();
$.ajax({
url: "portfolio/transactions/" + domain + "/",
type: "post",
data: serial, // serial instead of {data : serial}
success: function (dat) {
$('#transactions_div').html(dat);
}
});
});