Kendo UI Observable calculated property how to force change notification - mvvm

In cases using Kendo UI Observable (MVVM) for HTML element binding I occasionally use calculated values, but I can't find a way to force the binding to update when the dependent values change. A simple example:
<div id="test3">
1. <span data-bind="text: addr"></span><br />
2. <span data-bind="text: addr1"></span><br />
3. <span data-bind="text: addr2"></span><br />
</div>
<script>
viewModel = kendo.observable({
addr1: "",
addr2: "",
addr: function () {
return this.addr1 + ' ' + this.addr2;
},
load: function () {
this.set("addr1", "123 Main St");
this.set("addr2", "STE 101");
//need to let view model know to update addr binding
}
});
//to demonstrate the problem of notification bind here
kendo.bind($("#test3"), viewModel);
viewModel.load();
//if I bind here it works, of course
//kendo.bind($("#test3"), viewModel);
</script>
I have a work-around for this where I set the 'addr' element directly, but that defeats the point of data binding.

Change your addr function to use the 'get' function to retrieve the values:
addr: function () {
return this.get("addr1") + ' ' + this.get("addr2");
},
The 'get' function is part of the kendo observable framework. When used like this, kendo will then know to refresh the bound value from your function when any of the values within it, accessed via 'get', are 'set' elsewhere.

Related

In Reactjs how do I update the correct value in the parent state with a callback that's been passed to a nested child component?

I've been on this one for days, and all my reading hasn't helped me find a clean solution for this particular case.
Issue
I can send a parent state value and callback down to a nested component, but once the callback is triggered in the child I don't know how I can send the updated value back to the parent so it can update the correct value.
For instance
Parent Component (Has values and the callback)
Child Component (Values and callback is passed here)
Grand Child Component (Values Updated here and callback triggered)
What is SEEMS to cause the Issue
It seems the issue is I need the original key name in order for "setState" to update the correct value in the parent component(or at least it seems that way), but the child component only has original value and new updated value and has no access to the key associated with original value in the parent component.
Important Notes on Best Practice Surrounding this question
-From what I understand it is bad practice to use refs to handle nested situations like this.
-It seems like there is a cleaner solution than sending a prop for the key and another for the value.
-I'm assuming also that flux might provide a solution to this issue but I feel that there is a basic component to component communication technique or principle that I'm missing here.
Here is a bare bones example of what I'm dealing with.
/*All the values need to be updated here so that the inputs can used for calculation and then sent to a component that displays the output*/
var Calculator =
React.createClass({
getInitialState:function(){
return {
value1: "Enter value 1", /*These values are passed to a nested child component, can't figure how to update the right one*/
value2: "Enter value 2",
}
},
update: function(update){
this.setState(
update
);
},
render: function () {
return (
<div>
<h2>Input</h2>
<Input onClick={this.handleClick} update={this.update} value1={this.state.value1} value2={this.state.value2} /> //pass the values here
<h2>Output</h2>
<Output />
</div>
);
},
handleClick: function () {
//want to update the state for the correct value here
}
});
/* A compenent that is a middle layer between the parent and nested child component I'm working with*/
var Input =
React.createClass({
update: function(){
this.props.update();
},
render:function(){
return (
<div>
<p><InputComponent update={this.update} value={this.props.value1} /> / <InputComponent value={this.props.value2}/></p>//passing down values again
<p><ButtonComponent onClick={this.props.onClick} /></p>
</div>
)
}
});
/*This is the child component that gets the value and call back from the top level component. It will get updates to the values and send them back to change state of the parent component.*/
var InputComponent =
React.createClass({
handleChange: function(event) {
this.props.update();
},
render: function() {
return <input type="text" value={this.props.value} onChange={this.handleChange} />; //this props value has no key associated with it. Cant't make update object ie {originalkey:newValue}
}
});
/* This component is triggered to carry out calculations in the parent class.*/
var ButtonComponent =
React.createClass({
render:function(){
return <button onClick={this.handleClick}> {this.props.txt} </button>
},
handleClick: function(){
this.props.onClick();
}
});
/*The inputs will be calculated and turned to outputs that will displayed here.This component doesn't matter for the question so I left it empty*/
var Output =
React.createClass({
});
Here's an example I just put together on jsfiddle.
Instead of putting update in setState, we pass a value to update from the child component and let the parent set its state.
In the parent, we have:
_update: function(val){
this.setState({
msg: val
});
},
render: function() {
return (
<div>
<p>Message: {this.state.msg}</p>
<Child _update={this._update} />
</div>
);
}
And in the child, we have a _handleClick function that calls the parent _update function with values:
_handleClick: function(){
this.props._update(React.findDOMNode(this.refs.text).value);
},
render: function(){
return (
<div>
<input type="text" ref="text" />
<button onClick={this._handleClick}>Update</button>
</div>
);
}

React with server side variables

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.

How do I get element related to active input in jQuery UI Autocomplete?

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.

Kendo UI Grid popup editing with MVVM

I am using Kendo UI Grid and I have configured it to use popup editing with custom template
<script id="popup_editor" type="text/x-kendo-template">
<div id="editor">
<div class="k-edit-label">
<label for="Type">Type</label>
</div>
<select data-role="dropdownlist" data-value-field="Type" data-text-field="Type"
data-bind="source: typeSource, value: selectedProduct"></select>
<div class="k-edit-label">
<label for="Type">Option</label>
</div>
<select data-role="dropdownlist" data-value-field="Option" data-text-field="Option"
data-bind="source: productsSource.Options, value: selectedOption"></select>
</div>
</script>
This is my ViewModel:
function ViewModel() {
var getTypesUrl = "/Controller/Action";
var viewModel = kendo.observable({
typeSource: new kendo.data.DataSource({
transport: {
read: {
url: getConditionTypesUrl,
dataType: "json"
},
},
batch: true,
schema: {
model: {
id: "Type"
}
}
}),
selectedType: null,
selectedOption: null
});
kendo.bind($("#editor"), viewModel);
}
ViewModel();
My action returns JSON.
The problem is that when I click on the "Add new record" button, there is no call to the getTypesUrl and the dropdownlist is not populated. The general idea is to have different options for different types and to populate the Option dropdownlist depending on the selected type. I believe, that the problem occurs because the editor is showed only when the button is clicked and the kendo can not create the bindings.
If the Drop down list is the same for each row get its values from the Data Source and store these in a variable in the page in JavaScript and point the Drop Down list at this new Data Source. Use some JavaScript to associate the id and name.
Alternatively if this is loaded based on some other logic implement a separate call to populate the Data source for the drop down list in your view model.
http://www.aspnetwiki.com/page:introduction-to-kendo-mvvm
http://www.aspnetwiki.com/page:kendo-mvvm-ui-grid
Further note your can write your template purely in JavaScript and bind this to the html, advantage of which is you can debug it and it can still be loaded by an ajax call later and it is likely going to be smaller.

Mvvm with knockout : array binding and changing inner objects state

I have an array in my View Model. Items of this array are objects of Person that has two properties. when I bind this to a template it's okay. but when I change the state of one of the properties it does not reflect in UI.
what did I do wrong ?
<script type="text/html" id="person-template">
<p>Name: <span data-bind="text: name"></span></p>
<p>
Is On Facebook ?
<input type="checkbox" data-bind="checked: IsOnFacebook" />
</p>
</script>
<script type="text/javascript">
var ppl = [
{ name: 'Pouyan', IsOnFacebook: ko.observable(true) },
{ name: 'Reza', IsOnFacebook: ko.observable(false) }
];
function MyViewModel() {
this.people = ko.observableArray(ppl),
this.toggle = function () {
for (var i = 0; i < ppl.length; i++) {
ppl[i].IsOnFacebook = false;
}
}
}
ko.applyBindings(new MyViewModel());
</script>
when I press the button I want to make changes in People.IsOnFacebook property. the changes will be made successfully but the UI does not show.
You should call it like a function. Like:
ppl[i].IsOnFacebook(false);
This because the ko.observable() returns a function. It's not a property you call anymore but a function call. So in the background they will update your UI. To retreive a property that is observable. You should also use the function call.
Please see this tutorial: http://learn.knockoutjs.com/#/?tutorial=intro