I'm trying to set up a drag&drop component to upload multiple files. However, when I attempt to access elements on the DOM with the querySelector method, I end up with null.
I've tried to implement the AfterViewInit class to no avail. Here's my current dart code for the component:
import 'dart:html';
import 'package:dnd/dnd.dart';
import 'package:angular/angular.dart';
#Component(
selector: 'upload',
templateUrl: 'upload.html',
styleUrls: [
'upload.css'
]
)
class Upload implements AfterViewInit {
#override
void ngAfterViewInit() {
// TODO: implement ngOnInit
Draggable d = new Draggable(document.querySelectorAll('.page'), avatarHandler : new AvatarHandler.clone());
var del = document.querySelector('.upload');
print(del); //prints null
Dropzone dropzone = new Dropzone(document.querySelector('.upload')); //throws an error, as it doesn't expect null.
dropzone.onDrop.listen((DropzoneEvent event){
print(event);
});
}
}
Also, my upload.html file is as follows:
<div class="center-me page" uk-grid>
<div class="uk-align-center text-align-center">
<h2 class="text-align-center" >Upload a file</h2>
<div class="upload uk-placeholder uk-text-center">
<span uk-icon="icon: cloud-upload"></span>
<span class="uk-text-middle">Attach binaries by dropping them here or</span>
<div uk-form-custom>
<input type="file" multiple>
<span class="uk-link">selecting one</span>
</div>
</div>
<progress id="progressbar" class="uk-progress" value="0" max="100" hidden></progress>
</div>
</div>
Thanks in advance.
So this looks like it should work. I wouldn't actually suggest doing it this way as it will get any element with an upload class which if you reuse the component will be a lot.
I would suggest using the ViewChild syntax instead
class Upload implements AfterViewInit {
#ViewChild('upload')
void uploadElm(HtmlElement elm) {
Dropzone dropzone = new Dropzone(elm);
dropzone.onDrop.listen((DropzoneEvent event){
print(event);
});
}
}
In the template:
<div class="uk-placeholder uk-text-center" #upload>
That said you shouldn't be getting null from the querySelect, but from the code you have shown I'm not sure why.
Related
I was able to get the url in the attachment field but for the redux form it was empty, how is possible to pass the value of the url to the redux form? Below is the code and the screenshot:
<div className="FileUpload">
<Dropzone
onDrop={this.onImageDrop.bind(this)}
multiple={false}
accept="image/*">
<div>Drop an image or click to select a file to upload.</div>
</Dropzone>
</div>
<div className="form-group">
<label htmlFor="attachment">Attachment:</label><br />
<input className="form-control" focus placeholder="attachment" type="text" name="attachment" ref="attachment" value={this.state.uploadedFileCloudinaryUrl} />
{this.state.uploadedFileCloudinaryUrl === '' ? null :
<div>
<p>{this.state.uploadedFile.name}</p>
<img src={this.state.uploadedFileCloudinaryUrl} alt="" />
</div>}
</div>
<div className="ui small image">
<img src={this.props.workrequest.attachment} alt="" />
</div>
the url in the attachemnt field
The first one is using the React Dropzone to get the url but for the Redux Form it was empty. May I know how to do that to get the url inserts at Redux Form here? Thank you
import React from 'React';
import { connect } from 'react-redux';
import { change, reduxForm } from 'redux-form';
import Dropzone from 'react-dropzone';
class UploadForm extends React.Component {
onDrop = (accepted) => {
if (!accepted.length) return;
// start uploading
this.setState({ isUploading: true });
const formData = new FormData();
formData.append('file', accepted[0]);
axios.post('upload', formData).then(
(res) => {
this.props.dispatch(change('uploadForm', 'url', res.url));
this.setState({ isUploading: false });
},
() => {
this.setState({ isUploading: false });
}
);
};
render() {
return (
<form>
<Dropzone onDrop={this.onDrop} />
</form>
);
}
}
export default connect()(
reduxForm({
form: 'uploadForm',
initialValues: {
url: ''
}
})(UploadForm)
);
Please use this.
Actually, you must use Field component from Redux-form.
Other wise, you can change form values using dispatch and change action creator.
import { Field, change } from 'redux-form';
this.props.dispatch(change('uploadForm', 'url', this.state.url));
I try to implement async(isUnique) and sync(cannotContainSpace) validation functions for username field, I want to see "this username already in use alert msg, if textbox value is "yener". sync function works fine but i think ss below can help explain my issue;
Note:canNotContain sync validation func works fine, aafter I added isUnique async function this exception occured..
how can I fix it ?
usernamevalidator.ts
import {FormControl} from '#angular/forms';
export class UsernameValidator{
static isUnique(control:FormControl){
return new Promise((resolve, reject)=>{
setTimeout(function(){
debugger
if(control.value =="yener")
resolve({isUnique:true})
else
resolve(null)
},1000);
});
}
static cannotContainSpace(control:FormControl){
if(control.value.indexOf(' ')>=0)
return { cannotContainSpace:true };
return null;
}
}
postmessage.component.ts
import { Component } from '#angular/core';
import {FormControl,FormGroup,FormBuilder,Validators} from '#angular/forms';
import {UsernameValidator} from '../../validators/usernamevalidator';
#Component({
moduleId:module.id,
selector: 'post-message',
templateUrl: '../../templates/postmessage.component.html'
})
export class PostComponent {
form : FormGroup;
constructor(fb:FormBuilder){
this.form = fb.group({
username:['', Validators.compose([Validators.required, UsernameValidator.cannotContainSpace]),Validators.compose([UsernameValidator.isUnique])],
email:['', Validators.required],
message:['', Validators.required]
});
}
signup(){
console.log(this.form.value);
}
}
here is html template:
<form class="from-horizontal" [formGroup]="form" (ngSubmit)="signup()">
<div class="form-group row">
<label for="username" class="control-label col-md-2">Name:</label>
<div class="col-md-6">
<input type="text" id="username" class="form-control" formControlName="username">
<div *ngIf="form.controls['username'].touched && form.controls['username'].errors">
<div class="alert alert-danger"
*ngIf="form.controls['username'].errors.required">
User name is required.
</div>
<div class="alert alert-danger"
*ngIf="form.controls['username'].errors.cannotContainSpace">
User name can not contain space
</div>
<div class="alert alert-danger" *ngIf="form.controls['username'].errors.isUnique">
This user name already in use.
</div>
</div>
</div>...
it's strange, i though we can use "Validators.compose()" function in form builder initializer as async parameters but angular2 dont agree with me..
I just changed;
username:['', Validators.compose([Validators.required,
UsernameValidator.cannotContainSpace]),Validators.compose([UsernameValidator.isUnique])],
to
username:['', Validators.compose([Validators.required,
UsernameValidator.cannotContainSpace]),UsernameValidator.isUnique],
and it works
EDITED:
also I figured out if you want to use multiple async validators in a component use
Validators.composeAsync()
function.
I try to detect on change with dart. Example html:
<div>
<input id="photoUpload" type="file" name="photo" (onchange)="update()" multiple>
</div>
Dart:
#Component(
selector: "photo-upload-dialog", templateUrl: "photo_upload_dialog.html")
class PhotoUploadDialog {
update() async {
print('Changed!');
}
}
But nothing in a console.
onChange is the default event handler name, not the event name.
Use instead
(change)="update()"
I am using Angular2 and MVC. I am following along with a lot of the tutorial on angular.io but changing some things to implement my own solution. Basically I have a table that shows clients and their identifier number. Using mock data, I was able to retrieve the clients and add new ones. The new ones would update my clientList and show the new client in my display table.
Once I started converting this to hit the server to get and post the data, I started to have issues.
I am able to retrieve the data perfectly fine from the server and it gives me the output I expect. When I try to post a new one to the server, it goes into my post method perfectly fine. It pushes the value into my clientList, but doesnt update my display table.
I'd like to add that I am not actually adding the data to my list on the server side, I am simply just returning a new object to try to get it to update my view.
Here's my service:
import {Injectable} from 'angular2/core';
import {Client} from './client';
import {RequestOptions, Http, Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class ClientService {
constructor(private http: Http) { }
getClients(): Observable<Client[]> {
return this.http.get('/Client/GetClients')
.map(this.extractData);
}
addClient(client: Client): Observable<Client> {
let clientUrl = '/Client/AddClient';
let body = JSON.stringify({ client });
let header = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: header });
return this.http.post(clientUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if (res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || {};
}
private handleError(error: any) {
// In a real world app, we might send the error to remote logging infrastructure
let errMsg = error.message || 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
My app component:
#Component({
selector: 'my-app',
templateUrl: 'Scripts/typescript/app.component.html',
directives: [ClientFormComponent, MODAL_DIRECTIVES],
providers: [ClientService, HTTP_PROVIDERS]
})
#Injectable()
export class AppComponent implements OnInit {
constructor(private _clientService: ClientService) { }
currentClient: Client;
#ViewChild('modal')
modal: ModalComponent;
public clients: Client[];
public errorMessage: string;
ngOnInit() {
this.getClients();
}
getClients() {
this._clientService.getClients().subscribe(clients => this.clients = clients, error => this.errorMessage = <any>error);
}
deleteClient() {
var index = this.clients.indexOf(this.currentClient, 0);
if (index > -1) {
this.clients.splice(index, 1)
}
}
close() {
this.modal.close();
}
open(client: Client) {
this.currentClient = client;
this.modal.open();
And my component with the call to the service when I click an add button:
#Component({
selector: 'client-form',
templateUrl: 'Scripts/typescript/client-form.component.html'
})
export class ClientFormComponent
{
constructor(private _clientService: ClientService) { }
#Input() clientList;
model = <Client>{ name: 'Bob', npi: 12345678 };
submitted = false;
active = true;
errorMessage: string;
onSubmit() { this.submitted = true; }
ngOnInit() {
this.getClients();
}
getClients() {
this._clientService.getClients()
.subscribe(
clients => this.clientList = clients,
error => this.errorMessage = <any>error);
}
addClient() {
this._clientService.addClient(this.model)
.subscribe(
client => this.clientList.push(client),
error => this.errorMessage = <any>error);
}
}
Here is my template for displaying the table, which is the app.component.html:
<h1>Client/NPI Cross Reference</h1>
<client-form></client-form>
<table class="table">
<tr style="font-weight: bold;">
<td>Client</td>
<td>NPI</td>
<td>Date Added</td>
<td></td>
</tr>
<tr *ngFor="#client of clients;">
<td>{{client.name}}</td>
<td>{{client.npi}}</td>
<td>{{client.dateAdded}}</td>
<td>
<span style="color: red; font-size: 16px; cursor: pointer;" class="glyphicon glyphicon-trash" aria-hidden="true" (click)="open(client)"></span>
</td>
</tr>
</table>
<modal #modal [animation]="animationsEnabled" (onClose)="deleteClient()" (onDismiss)="dismissed()">
<modal-header [show-close]="true">
<h4 class="modal-title">Delete</h4>
</modal-header>
<modal-body>
Are you sure you want to delete this entry?
</modal-body>
<modal-footer>
<button type="button" class="btn btn-primary" (click)="modal.close()">Yes</button>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">No</button>
</modal-footer>
</modal>
Here is my form template html:
<div class="container">
<form *ngIf="active" (ngSubmit)="onSubmit()" #clientForm="ngForm">
<div class="form-group" style="float: left;">
<label for="clientid">Client Id:</label>
<input type="text" class="form-control" required [(ngModel)]="model.name" ngControl="name" #name="ngForm" #newClient>
<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
Name is required
</div>
</div>
<div class="form-group" style="float: left; padding-left: 10px;">
<label for="npi">NPI:</label>
<input type="number" class="form-control" required [(ngModel)]="model.npi" ngControl="npi" #newNpi>
</div>
<div style="float: left; margin: 25px 0 0 10px;">
<button type="submit" class="btn btn-default" (click)="addClient()" [disabled]="!clientForm.form.valid">Add</button>
</div>
</form>
</div>
<br style="clear:both;" />
On the server side, I am simply just returning a json client:
[HttpPost]
public JsonResult AddClient()
{
var client = new Models.Client();
client.name = "test";
client.npi = 12345;
client.dateAdded = DateTime.Now.ToShortDateString();
return Json(client);
}
When using the mock data, it would automatically update my table and I'd be able to see my new client in the table. But now that I am using the server, it adds it to the clientList, but it doesnt actually change the view of my table.
How can I get it to change my table and update it to show it's been added?
Maybe the point is that you are calling twice the method getClients() of your ClientService, once from the the AppComponent and once from the ClientFormComponent.
This means that you will get 2 different arrays, one held in the AppComponent and one held in the ClientFormComponent.
If what I say is correct, then when you add a new client via the POST command, then the ClientFormComponent updates its array but this has no effect on the AppComponent array (which is a different object).
The solution in this case is probably:
1) make the AppComponent pass its clients array to the ClientFormComponent such as <client-form [clientList]="clients"></client-form> (which is probably the reason why clientList is preceded by #Input())
2) Avoid like hell resetting the clientList from within the ClientFormComponent (which is currently happening in the line this.clientList = clients)
I hope I am not missing something and that this can help.
PS: I guess you do not need #Injectable() in the AppComponent, since it is a Component
Here is what I think is happening. When you call getClients() inside ClientFormComponent, it replaces the clientsList with a new array. But, the one in the view is the array assigned by getClients() inside AppComponent. One way to make sure is to comment the line inside ClientFormComponent:
ngOnInit() {
//this.getClients();
}
Change the line:
public clients: Client[];
to
public clients: Client[] = [];
A short background:
This example is a slightly more complicated version of my Angular Dart: Data binding doesn't work when manipulating the controller from the outside question that has been answered correctly. I only added a toggleable "show resolved comments" link to this version. Even though I initialized every variable to non-null values the problem still happens.
Full description of the actual problem:
I have two controllers nested into each other. The outer controller shows/hides the inner controller by using an ng-switch directive.
The outer controller also contains a checkbox. If this checkbox gets checked then the inner controller is made visible (via the above ng-switch directive). This checkbox works as intended.
There's also an "open" link outside the controllers. Its onclick handler calls into the outer controller and is supposed to check the checkbox via the model. The problem is that even though the model gets changed, the view doesn't get updated, unless I explicitly call scope.apply(), which I shouldn't. Even if I remove the comment before scope.apply() in my code then data binding doesn't work within InnerController.
This pattern has worked flawlessly in AngularJS but apparently doesn't in AngularDart.
I insist to this pattern or something similar because I'm in the process of integrating AngularDart into a legacy application that doesn't use data binding so I have to trigger model changes from outside the models.
Thanks in advance!
<html ng-app>
<head>
<title>Angular.dart nested controllers</title>
</head>
<body>
open
<div outer-controller ng-switch="outerCtrl.showInnerController">
<input type="checkbox" ng-model="outerCtrl.showInnerController">
<div inner-controller ng-switch-when="true">
Your name: <input ng-model="innerCtrl.yourName">
<br>
Hello {{innerCtrl.yourName}}!
<div ng-switch="innerCtrl.showResolvedComments" style="text-decoration:underline; color:blue; cursor:pointer">
<div ng-switch-when="true" ng-click="innerCtrl.showResolvedComments = false">Hide resolved comments</div>
<div ng-switch-when="false" ng-click="innerCtrl.showResolvedComments = true">Show resolved comments</div>
</div>
</div>
<div inner-controller ng-switch-when="false">
other controller
</div>
</div>
<script type="application/dart">
import "dart:html";
import 'package:angular/angular.dart';
import 'package:angular/application_factory.dart';
OuterController outerController;
#Controller(selector:'[outer-controller]', publishAs:'outerCtrl')
class OuterController {
bool showInnerController = false;
Scope scope;
OuterController(this.scope) {
outerController = this;
}
void showOuterController() {
showInnerController = true;
//scope.apply();
}
}
#Controller(selector:'[inner-controller]', publishAs:'innerCtrl')
class InnerController {
String yourName = 'defaultName';
bool showResolvedComments = true;
}
class MyAppModule extends Module {
MyAppModule() {
type(InnerController);
type(OuterController);
}
}
main() {
applicationFactory().addModule(new MyAppModule()).run();
querySelector('#open').onClick.listen((Event event) {
outerController.showOuterController();
});
}
</script>
</body>
</html>
After some experimentation, it's look like angular listen specified event to activate ng-model, and it doesn't look every variable change, i think because it's complicated to watch every change in variable without impact performance.
You can change your approach by simulate a user click on the check box
like:
CheckboxInputElement checkBox = querySelector("input");
if (checkBox.checked == false) {
checkBox.click();
}
It's maybe not the cleaner way to do this, but it works
Here the full code with the patch
<html ng-app>
<head>
<title>Angular.dart nested controllers</title>
</head>
<body>
open
<div outer-controller ng-switch="outerCtrl.showInnerController">
<input type="checkbox" ng-model="outerCtrl.showInnerController">
<div inner-controller ng-switch-when="true">
Your name: <input ng-model="innerCtrl.yourName">
<br>
Hello {{innerCtrl.yourName}}!
<div ng-switch="innerCtrl.showResolvedComments" style="text-decoration:underline; color:blue; cursor:pointer">
<div ng-switch-when="true" ng-click="innerCtrl.showResolvedComments = false">Hide resolved comments</div>
<div ng-switch-when="false" ng-click="innerCtrl.showResolvedComments = true">Show resolved comments</div>
</div>
</div>
<div inner-controller ng-switch-when="false">
other controller
</div>
</div>
<script type="application/dart">
import "dart:html";
import 'package:angular/angular.dart';
import 'package:angular/application_factory.dart';
OuterController outerController;
#Controller(selector:'[outer-controller]', publishAs:'outerCtrl')
class OuterController {
bool showInnerController = false;
Scope scope;
OuterController(this.scope) {
outerController = this;
}
void showOuterController() {
showInnerController = true;
print("showOuterController");
//scope.apply();
}
}
#Controller(selector:'[inner-controller]', publishAs:'innerCtrl')
class InnerController {
String yourName = 'defaultName';
bool showResolvedComments = true;
}
class MyAppModule extends Module {
MyAppModule() {
type(InnerController);
type(OuterController);
}
}
main() {
applicationFactory().addModule(new MyAppModule()).run();
querySelector('#open').onClick.listen((Event event) {
outerController.showOuterController();
// Added Code
CheckboxInputElement checkBox = querySelector("input");
if (checkBox.checked == false) {
checkBox.click();
}
// End added code
});
}
</script>
</body>
</html>