在我的component.ts
文件中,我将值添加到属性中:
ngOnInit() {
this.route.params.subscribe(params => {
this.param = params['id'];
this.pagesService.getEditPage(this.param).subscribe(page => {
this.page = page;
});
});
}
但是在模板中,我有一个带有文本输入和textarea的表单,该表单的行为就好像它没有任何值。
这是表格代码:
<form novalidate #f="ngForm" (ngSubmit)="editPage(f)">
<div class="form-group">
<label for="">Title:</label>
<input type="text" class="form-control" placeholder="Title" name="title" value="{{page?.title}}" [(ngModel)]="title" #pageTitle="ngModel"
minlength="2" required />
</div>
<div class="alert alert-danger" *ngIf="pageTitle.errors?.required && pageTitle.touched">
Title is required.
</div>
<div class="alert alert-danger" *ngIf="pageTitle.errors?.minlength && pageTitle.touched">
Minimum length is 2.
</div>
<div class="form-group">
<label for="">Content:</label>
<textarea class="form-control" name="content" placeholder="Content" cols="30" rows="10" [(ngModel)]="content" #pageContent="ngModel"
minlength="5" required>{{ page?.content }}</textarea>
</div>
<div class="alert alert-danger" *ngIf="pageContent.errors?.required && pageContent.touched">
Content is required.
</div>
<div class="alert alert-danger" *ngIf="pageContent.errors?.minlength && pageContent.touched">
Minimum length is 5.
</div>
<div class="form-group">
<button class="btn btn-default" [disabled]="!f.valid">Edit page</button>
</div>
</form>
当显示表单时,文本输入具有正确的值,但它的行为就好像没有,因为它的验证启动了,而textarea根本没有显示真实内容,它显示了占位符内容(但是当我检查textarea时,我可以看到它的真实内容。)
是因为在获取数据和显示表单之间有一些延迟还是什么?以及如何解决这个问题?
答案 0 :(得分:1)
您通过同时使用 fabric.Object.prototype.transparentCorners = false;
canvas = this.__canvas = new fabric.Canvas('c', {
backgroundColor: '#333',
HOVER_CURSOR: 'pointer'
});
fabric.Polaroidphoto = fabric.util.createClass(fabric.Image, {
type: 'polaroidphoto',
H_PADDING: 20,
V_PADDING: 20,
originX: 'center',
originY: 'center',
initialize: function(src, options) {
this.image = new Image();
this.image.src = src;
this.callSuper('initialize',src, options);
console.log("initialize, src:" + src);
this.image.onload = (function() {
this.width = this.image.width;
console.log("initialize, scaleX:" + this.image.scaleX);
this.height = this.image.height;
this.src= this.image.src;
console.log("initialize image.onload, src:" + src);
this.loaded = true;
this.setCoords();
this.fire('image:loaded');
}).bind(this);
},
_render: function(ctx) {
if (this.loaded) {
console.log("_render:is_loaded");
ctx.fillStyle = '#fff';
ctx.fillRect(
-(this.width / 2) - this.H_PADDING,
-(this.height / 2) - this.H_PADDING,
this.width + this.H_PADDING * 2,
this.height + this.V_PADDING * 2);
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
} else {
console.log("_render:is_NOT__loaded");
}
}
});
// Added fromObject function for sub-class
fabric.Polaroidphoto.async = true;
fabric.Polaroidphoto.fromObject = function (object, callback) {
console.log("fabric.Polaroidphoto.fromObject object.src) :" + object.src);
var instance = new fabric.Polaroidphoto(object.src, object);
callback && callback(instance);
// added handler to render instance
instance.on('image:loaded', when_loaded );
};
var photo = new fabric.Polaroidphoto('https://i.stack.imgur.com/cqmQ9.png', { });
photo.on('image:loaded', when_loaded );
photo.set('scaleX', 1);
photo.set('scaleY', 1);
photo.set('top', 180);
photo.set('left', 150);
canvas.add(photo);
canvas.add(
rect= new fabric.Rect({ top: 50, left: 100, width: 50, height: 50, fill: '#f55' }),
circle = new fabric.Circle({ top: 140, left: 230, radius: 75, fill: 'green' }),
triangle = new fabric.Triangle({ top: 300, left: 210, width: 100, height: 100, fill: 'blue' })
);
// required at load to render sub-classed image in group
function when_loaded() {
console.log("when_loaded");
dirty(); // to set dirty : true
canvas.renderAll();
}
// required at load to display sub-classed image in group,
// set dirty:true for groups
function dirty() {
$.each(canvas._objects, function( index, obj ) {
if( typeof obj.type !== 'undefined' && obj.type == 'group') {
obj.dirty= true;
}
});
}
$("#group").on('click', function() {
var activegroup = canvas.getActiveGroup();
var objectsInGroup = activegroup.getObjects();
activegroup.clone(function(newgroup) {
canvas.discardActiveGroup();
objectsInGroup.forEach(function(object) {
canvas.remove(object);
});
canvas.add(newgroup);
newgroup.dirty = true;
});
});
$("#ungroup").click(function(){
var activeObject = canvas.getActiveObject();
if(activeObject.type=="group"){
var items = activeObject._objects;
alert(items);
activeObject._restoreObjectsState();
canvas.remove(activeObject);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
items[i].dirty = true;
canvas.item(canvas.size()-1).hasControls = true;
}
canvas.renderAll();
}
});
和ngModel
来滥用模板驱动的表单。您还错过了关于Angular的整个想法,您的模型定义了应用程序的状态,模板是一种装饰性地表达如何呈现该模型的方式。
你根本不应该使用value
。使用value
,您已经正确地在您已经放置此指令的输入控件和您的类中的变量[(ngModel)]="title"
之间创建了双向绑定。如果您希望更新输入,则应更改模型,并自动更新视图。因此,在title
中,您应该执行以下操作。
.subscribe
Angular会注意到更新this.title = page.title
,因为title
,该更改会在视图中显示出来。
与您的问题无关,但您也在滥用RxJS。嵌套订阅是反模式和潜在的内存泄漏。要make dependent requests using RxJS,您可以使用switchMap
operator。
[ngModel]="title"
如果由于某种原因需要保存this.route.params
.map(params => params.id) // or .pluck('id'), but you lose type safety
.switchMap(params => this.pageService.getEditPage(params))
.subscribe(page => this.page = page);
的中间结果,可以使用params['id']
运算符,或者在从.do
内的lambda返回之前保存它。