Forms in reactjs with flux - forms

I have a form, this form needs to post some data to my backend. With flux, what is the best practice for doing this, use a store?
My issue with using a store is that I have a sub component inside of my form that allows me to select a number 1-5 with buttons. I wanted that component to be reusable, but if i use a store, I have to hard code the store into the child component which means I cant really use it elsewhere. Instead do I just set the parent state from the child?
If anyone can point out some good tutorials or examples of react/flux forms let me know.

In my opinion any back end interaction should be done by using actions, but...
if you want to use store anyway then you can create additional attribute (prop) in your sub-component which will be a function (f.e. onChange) which should be passed from parent component as prop (this function should set data in store). Then you can reuse this component, because only parent needs to have access to store.
So in subcomponent:
onButtonClick(e) {
this.state.value = e.target.value;
if (this.props.onChange) this.props.onChange(e.target.value);
}
<div>
<button onClick={this.onButtonClick.bind(this)} value="1">1</button>
<button onClick={this.onButtonClick.bind(this)} value="2">2</button>
<button onClick={this.onButtonClick.bind(this)} value="3">3</button>
<button onClick={this.onButtonClick.bind(this)} value="4">4</button>
<button onClick={this.onButtonClick.bind(this)} value="5">5</button>
</div>
and in parent:
setMyStoreState(value) {
store.setNumber(value);
}
<Subcomponent onChange={this.setStoreState.bind(this)} />
or something like this.
Code not tested, but you should get the idea.

Related

Change Component's content during rendering phase when loading its model fails

I have a widget components with simple markup inheritance like so
AbstractWidget
<wicket:panel>
<wicket:child />
<div wicket:enclosure="editButton" class="widget-edit-wrapper">
<button wicket:id="editButton" type="button" class="widget-edit">
<span class="glyphicon glyphicon-cog"></span>
</button>
<div style="display:none;">
<div wicket:id="editPanel" class="widget-settings"></div>
</div>
</div>
</wicket:panel>
LabelWidget
<wicket:extend>
<div wicket:id="container" class="label-widget flex-container">
<div wicket:id="label"></div>
</div>
</wicket:extend>
Now imagine the label content is taken from a loadable detachable model and loading the model throws an Exception.
I need to show some feedback to the user on this 'broken' widget component. Is there a way to replace the whole child's content when loading its model throws an Exception?
Note that LabelWidget is just one from many AbstractWidget childs so I need to solve this in the AbstractWidget and I also need to preserve all elements from the AbstractWidget component.
You can accomplish this by using a smarter model - a model that delegates to the original one and try/catches if it throws an exception. In case of an exception you will need to return an "empty" model object, where "empty" would mean different things for your different use cases.
Your smart model could implement IComponentAssignedModel so that it knows the Component it is used in. This way in the catch clause you can do component.error("..."). In AbstractWidget you should add a FeedbackPanel that will render the error message. The specialization widget, like LabelWidget, will render as "empty" (whatever this means for it) by using the fallback model.

angularjs2: setting fields to "dirty", especially datepicker

I am new to Angular2/Typescript, since I come from the Java world, I decided to learn Typescript and Angular2 directly.
I want to leave most of the logic on the server, thus I don't need complex validation management on the client. So all I want is the user to fill out forms, and post/put all the fields to the REST Service.The goal is to leave the client side as light as possible.
I have a form:
<form role="form" (ngSubmit)="onSubmit()" #ArbeitstagForm="ngForm">
and a field in it, some datepickers too: similar like this:
<input type="text" class="form-control pull-right" id="datepicker" [(ngModel)]="model.datum">
When I submit the form, I call this function:
model = new Arbeitstag();
onSubmit(form:any) {
alert(JSON.stringify(this.model));return false;
}
So that alerts me the the entered data as JSON, which I will after send to a REST Service. It works actually great, BUT only when I actually type something into the field, when I have a default value, or I set the field with a datepicker, the model object values will remain empty.
I've found out about the dirty setting of the fields, which are false by default and are getting true when I type something in and that's also what I see when I check firebug, but that's definitely not what I want to achieve.
Is there a way to set all the fields dirty in a form in Angular2? I've found many examples for Angular.js 1, but not for Angular2/Typescript.
Control has a markAsDirty() (and markAsTouched()) method
<input #datePicker="ngForm" type="text" class="form-control pull-right" id="datepicker" [(ngModel)]="model.datum">
<button (click)="datePicker.control.markAsDirty()">update dirty status</button>
Plunker example
What I usually do is get a reference to the form in my component, using ViewChild. With that reference I can mark to form dirty or touched when I need to. Like so:
export class MyComponent implements OnInit, OnDestroy {
#ViewChild('form') form: NgForm;
...
public methodWithFormChange(): void {
this.form.control.markAsDirty();
}
}
;-)

AngularJS retrieve from object based on entry in ng-repeat input

This application is for running a writing contest.
Coodinators are assigning entries to judges for them to judge. I have three sets of data I retrieve from the server, a judge list, an entries list and an assignment list that ties the two together. There can be a variable number of input fields...if a judge has agreed to judge 4 entries, there will be 4 inputs...if 7, then 7.
I have all of that working OK, but only insofar as the entry number can be input and the data updated.
Now I would like confirm that the entryID IS a valid ID by checking the list and also to show a field or two on the screen so the coordinator knows that they typed in the right entry.
The relevant section of the HTML
<div ng-app>
<div id="assignment" ng-controller="AssignData" ng-init="JudgeID=107;CategorySelect='MS';PublishSelect='P'">
<div ng-show="loaded">
<form class="entryform ng-cloak" name="assignform" ng-submit="sendForm()">
<p>Entry numbers assigned to this judge</p>
<p ng-repeat="assign in (formassigns =(assigns | filter:AssignedJudge))">
<input type="text" ng-model="assign.entryid" required/>
{{entries.authorname}} {{entries.entrytitle}}
</p>
<button type="submit">Save Assignments</button>
<p>This will keep the assignments attached to this judge.
You will be able to send all of your assignments to all
of your judges when you are finished.</p>
</form>
</div>
</div>
</div>
The part that I haven't been able to figure out is how to make entries.authorname and entries.entrytitle show up when the user types in an entryid that is in entries.entryid.
assigns and entries are both arrays of records using JSON
assigns is JSON made up of assigns.id, assigns.judgeid, assigns.entryid.
entries is JSON made up of entries.entryid, entries.entrytitle, entries.authorname
When assigns arrives, entryid is empty. The form is used to fill in the entryid and when it is filled in, I'd like to be able to show next to it the title and authorname for that entry.
NOTE: I've added some important information at the end of this answer. So please read to the end before you decide what you're going to do.
You're going to have to do something that does the look up.
Also a few other changes I'd add, mostly so you can actually validate the items in your repeat.
(There's a summary of what I did after the psuedo code below).
<div ng-app>
<div id="assignment" ng-controller="AssignData"
ng-init="JudgeID=107;CategorySelect='MS';PublishSelect='P'">
<div ng-show="loaded">
<form class="entryform ng-cloak" name="assignform" ng-submit="sendForm()">
<p>Entry numbers assigned to this judge</p>
<p ng-repeat="assign in (formassigns =(assigns | filter:AssignedJudge))"
ng-form="assignForm">
<input type="text" ng-model="assign.entryid"
ng-change="checkEntryId(assign, assignForm)"
name="entryid" required/>
<span ng-show="assignForm.entryid.$error.required">required</span>
<span ng-show="assignForm.$error.validEntry">
{{assignForm.$error.validEntry[0]}}</span>
{{assign.entry.authorname}} {{assign.entry.entrytitle}}
</p>
<button type="submit">Save Assignments</button>
<p>This will keep the assignments attached to this judge.
You will be able to send all of your assignments to all
of your judges when you are finished.</p>
</form>
</div>
</div>
</div>
Then in your controller, you'd add a function like so (be sure to inject $http or a service you wrote to pull the values from the server):
$scope.checkEntryId = function(assign, form) {
$http.get('/CheckEntry?id=' + assign.entryid,
function(entry) {
if(entry) {
assign.entry = entry;
form.$setValidity('validEntry', true);
} else {
form.$setValidity('validEntry', false, 'No entry found with that id');
}
}, function() {
form.$setValidity('validEntry', true, 'An error occurred during the request');
console.log('an error occurred');
});
};
The basic idea above:
Use ng-form on your repeating elements to allow for validation of those dynamic parts.
Create a function that you can pass your item and your nested form to.
In that function, make your AJAX call to see if the entry is valid.
Check the validity based on the response, and call $setValidity on your nested form you passed to the function.
Use ng-show on a span (or something) in your nested form to show your validation messages.
Also, assign your checked entry to your repeated object for display purposes. (you could use a seperate array if you want, I suppose, but that would probably get unnecessarily complicated).
I hope that helps.
EDIT: Other thoughts
You might want to wrap your call in a $timeout or some sort of throttling function to prevent the entry id check from spamming yoru server. This is an implementation detail that's totally up to you.
If this is a check you do all over the place, you'll probably want to create a directive to do it. The idea would be very similar, but you'll do the check inside of a $parser on the ngModelController.
The method I showed above will still actually update the model's entryid, even if it's invalid. This is usually not a big deal. If it is, you'll want to go with what I suggested in "other thought #2", which is a custom validation directive.
If you need more information about validation via custom directives I did a blog entry on that a while back

Listening to click event on ListView

Feels like I'm missing something stupid here, but what's the recommended method to listen to the click event on a listview?
At the moment I've got:
WinJS.Utilities.query(".menuHolder").listen("click", linkClickHandler, false);
And my listview template uses the class 'menuHolder' for it's items:
<div id="menuTemplate"
data-win-control="WinJS.Binding.Template">
<div class="menuHolder">
<!-- menu img -->
<img src="#" data-win-bind="src : pic; alt : title" />
<div class="menuText">
<!-- menu text -->
<h1 data-win-bind="innerText : title"></h1>
<!-- menu desc -->
<h4 data-win-bind="innerText : description"></h4>
</div>
</div>
</div>
I don't seem to hit my breakpoint, in my link handler, or invoke it's function. Any thoughts?
EDIT:
As a follow on question (bearing in mind the item invoked event) is anyone aware of the recommended approach to pass data between a listview and the iteminvoked event, if I say wanted to use the WinJS.Navigator class to move around an application? I'm guessing I need to cast some part of the eventInfo into a suitable object and retrieve information, what part?
Assuming the data you want to "pass" is the data that is bound to the item that was invoked, you can do that in the event arguments that are passed in to the iteminvoked event. One of mine looks like this...
demosLV.oniteminvoked = function(e) {
e.detail.itemPromise.then(function(item) {
var location = format("/pages/{0}/{0}.html", item.data.key);
WinJS.Navigation.navigate(location, item.data);
});
};
So the demosLV is the ListView. I'm setting the oniteminvoked to a function. That function receives "e" as the event args. In the function I access e.detail.itemPromise and hang a .then off of it. Then I access the actual data in the .then using item.data.
Hope that's what you meant. BTW, the format function is one of mine in case you're wondering why it doesn't work for you.
Seems I was being a sausage, I needed to listen for the 'iteminvoked' event on the parent listview id reference, not the child level.
WinJS.Utilities.query("#menu").listen("iteminvoked", linkClickHandler, false);

MVC3 and Razor - How to place a dynamic value for hidden field?

I'm a beginner about Razor, and sometimes I get stuck with really simple things.
I have this foreach loop:
#foreach (dynamic item in ViewBag.EAList)
{
<li>
#using (#Html.BeginForm("Duplicate, "Daily"))
{
<p>#item.AuthorComment</p>
#Html.Hidden("EstadoDeAlmaID", #item.EAID)
#Html.Hidden("PosterID", Session["id"].ToString())
<input type="submit" value="Send" />
}
</li>
}
This line:
#Html.Hidden("EstadoDeAlmaID", #item.EAID)
Doesn't work, and I don't know how to make it work, I tried many ways, without #, with (--), with #(--)...
Could someone help me to display the dynamic value in my hidden field?
In addition, if someone know about a good Razor samples websites, I would be very thankful.
I had the same problem, found that a simple cast solved my problem.
#Html.Hidden("id", (string) ViewBag.ebook.isbn)
In Razor, once you are in "C# land", you no longer need to prefix values with # sign.
This should suffice:
#Html.Hidden("EstadoDeAlmaID", item.EAID)
Check out Scott Gu's article covering the syntax for more help.
Update
And I would also move your <li></li> within your using block, as Razor works better when you wrap HTML blocks inside of a code blocks.
Also, your Html.BeginForm should live outside of your loop.
#using (#Html.BeginForm("Duplicate, "Daily"))
{
<ul>
#foreach (? item in ViewBag.EAList)
{
<li>
<p>#item.AuthorComment</p>
#Html.Hidden("EstadoDeAlmaID", item.EAID)
#Html.Hidden("PosterID", Session["id"].ToString())
<input type="submit" value="Send" />
</li>
}
</ul>
}
Where ? in the foreach loop is the type of your items in EAList.
To avoid the Extension methods cannot be dynamically dispatched exception, use a model instead of ViewBag so you will not be using dynamic objects (this will avoid all the unnecessary casting in the View and is more in line with MVC style in general):
In your action when you return the view:
return View("ViewName", db.EAList.ToList());
In your view, the first line should be:
#model IEnumerable<EAListItem> //or whatever the type name is
Then just do:
#foreach(var item in Model)
You got the error, "Extension methods cannot be dynamically dispatched"... therein lies your trouble.
You should declare you loop variable not to be of type dynamic, but of the actual type in the collection. Then remove the # from the item.EAID call inside the #Html.Hidden() call.
The simple solution for me was to use ViewData instead of ViewBag. ViewBag is just a dynamic wrapper around ViewData anyway.
#Html.Hidden("ReportID", ViewData["ReportID"])
but I don't know if this will help in your case or not since you are creating dynamic items in your foreach loop.
I have found that when i want to use the view bag data in the HTML
Getting back to basics has often worked for me
<input type="hidden" name="Data" id="Data" value="#ViewBag.Data" />
this gave the same result.