I have a custom component that is created via as:
public function myFunc():void {
//some code
}
public function createComp():void {
var myVar:customComp = new customComp();
myVar.button01.label = "Some label";
PopUpManager.addPopUp(myVar, this, true);
}
When a button is pressed (button01) on that component, I want it to call the myFunc function on the parent component. Keep in mind that all of this is in as. Where do I add the event listener?
FIOFM, from this reference: http://www.mail-archive.com/flexcoders#yahoogroups.com/msg86364.html
In PopUp, create a new function variable, then create a button handler function, like this:
<fx:Script>
<![CDATA[
public var onSubmit:Function;
public function buttonHandler():void {
onSubmit.call();
}
...
In parent, add a public function:
public function openCustComp():void
{
var custComp:panelComp = new panelComp();
PopUpManager.addPopUp(custComp, this, true);
custComp.onSubmit = clearCISD;
PopUpManager.centerPopUp(instSuppDiaAdd);
}
Related
I'm using #types/xrm and attempting to test a method call with sinon. Unfortunately I am hitting quite a few issues due to the complex nature of the return and call I need to mock. I can find really simple examples of sinon stubbing or spying on calls, but nothing more complex than that.
I have the following simple code:
export class AccountForm {
static Fields = class {
static PrimaryContact = "primarycontactid";
static Name = "name";
}
public onLoad(context: Xrm.Events.EventContext): void {
// Get the form context
const formContext = context.getFormContext();
// Get the attributes required
const primaryContact = formContext.getAttribute(AccountForm.Fields.PrimaryContact);
const name = formContext.getAttribute(AccountForm.Fields.Name);
// Add our onchange events
primaryContact.addOnChange(this.onChangePrimaryContact);
name.addOnChange(this.onChangeName);
}
public async onChangePrimaryContact(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something");
}
public async onChangeName(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something else");
}
}
I want to test that an onchange event has been registered to both fields. Ideally, I'd like to check it's the RIGHT onchange, but I'll settle with the fact that it's been called once.
The "easy" way has been to check that the addOnChange method was called twice, this is as below:
import {AttributeMock, XrmMockGenerator} from "xrm-mock";
import * as sinon from "sinon";
import { AccountForm } from "../../src/entities/Account/Form";
describe("Account Form Tests", () => {
describe("Check Onload", () => {
beforeEach(() => {
XrmMockGenerator.initialise();
XrmMockGenerator.Attribute.createString("name", "");
XrmMockGenerator.Attribute.createLookup("primarycontactid", []);
});
it("should register onChange functions", () => {
// Arrange
let formContext = XrmMockGenerator.getFormContext();
let context = XrmMockGenerator.getEventContext();
// Stub
const attributeStub = sinon.stub(AttributeMock.prototype, "addOnChange");
// Act
let form = new AccountForm();
form.onLoad(context);
// Assert
expect(attributeStub.calledTwice).toBeTruthy();
});
});
});
But this is not very resilient, as it is not checking WHICH attributes the onChange functions were added to, or what function was registered.
I've tried stubbing the ForContext's "GetAttribute", but looks like it's requiring me to mock the entire return object, as otherwise, the stub does not return anything? I can get around this with using spy, but still can't work out how to check the attribute that the onChange is being added to and what the function is
Am I missing something obvious here?
I have Label in view, I need that Label's view in my ViewModel. I am using Dependency Service to set focus on Controls for Accessibility service, DS requires view as a param.
This is my Label
<Label
AutomationProperties.IsInAccessibleTree="{Binding ShowNoResults}"
IsVisible="{Binding ShowNoResults}"
Text="{Binding ResultsHeader}"/>
I tried Command but Label doesn't support command. Below code also not working
var view = GetView() as HomeworkView;
I am getting view always null. How can I fix this?
I am not quite sure what are you trying to achieve, but you can't access the View elements from you view model.
If you want to do something with the control, you can use the messaging center to do it, here is an example
in your ViewModel
MessagingCenter.Send(this, "your message here");
then in your page, you need to subscribe to this message from that view model and do the desired action
MessagingCenter.Instance.Unsubscribe<ViewModelClassNamedel>(this, "your message here");
MessagingCenter.Instance.Subscribe<ViewModelClassName>(this, "your message here", (data) =>
{
this.youControlName.Focus();
});
More detail added to Mohammad's answer.
Message Center doc.
In your ViewModel (with class name "YourViewModel"):
// Here we use control name directly.
// OR could make an "enum" with a value for each control.
string controlName = ...;
MessagingCenter.Send<YourViewModel>(this, "focus", controlName);
then in your page, subscribe to this message and do the desired action
.xaml.cs:
protected override void OnAppearing() {
{
base.OnAppearing();
// Unsubscribe before Subscribe ensures you don't Subscribe twice, if the page is shown again.
MessagingCenter.Instance.Unsubscribe<YourViewModel>(this, "focus");
MessagingCenter.Instance.Subscribe<YourViewModel>(this, "focus", (controlName) =>
{
View v = null;
switch (controlName) {
case "name1":
v = this.name1;
break;
case "name2":
v = this.name2;
break;
}
if (v != null) {
//v.Focus();
// Tell VM to use v as view.
((YourViewModel)BindingContext).SetFocus(v);
}
});
}
protected override void OnDisappearing() {
MessagingCenter.Instance.Unsubscribe<YourViewModel>(this, "focus");
base.OnDisappearing();
}
If need to pass View v back to VM, because that has the logic to use it:
public class YourViewModel
{
public void SetFocus(View view)
{
... your code that needs label's view ...
}
}
Not tested. Might need some slight changes. Might need
...(this, "focus", (sender, controlName) =>
instead of
...(this, "focus", (controlName) =>
UPDATE
Simple approach, if there is only ONE View that is needed in VM.
public class YourViewModel
{
public View ViewToFocus { get; set; }
// The method that needs ViewToFocus.
void SomeMethod()
{
...
if (ViewToFocus != null)
... do something with it ...
}
}
public class YourView
{
public YourView()
{
InitializeComponent();
...
// After BindingContext is set.
((YourViewModel)BindingContext).ViewToFocus = this.yourLabelThatShouldBeFocused;
}
}
ALTERNATIVE: It might be cleaner/more robust to set ViewToFocus in page's OnAppearing, and clear it in OnDisappearing. This ensures it is never used while the page is not visible (or in some delayed action after the page has gone away). I would probably do it this way.
protected override void OnAppearing()
{
base.OnAppearing();
((YourViewModel)BindingContext).ViewToFocus = this.yourLabelThatShouldBeFocused;
}
protected override void OnDisappearing()
{
((YourViewModel)BindingContext).ViewToFocus = null;
base.OnDisappearing();
}
is there any way how to detect that calendar has new month view is loaded, when "yellow buttons" (see screenshot) are used?
You can listen for click events of those buttons on the calendar using Renderer2.
Code from this blog post.
constructor( private renderer: Renderer2g) { }
ngAfterViewInit() {
let buttons = document.querySelectorAll('mat-calendar .mat-calendar-previous-button,' +
'mat-calendar .mat-calendar-next-button');
if (buttons) {
Array.from(buttons).forEach(button => {
this.renderer.listen(button, "click", () => {
console.log("Month changed");
});
})
}
}
You can just use your own header component with whatever buttons and events your want:
<mat-calendar [headerComponent]="headerComponent"></mat-calendar>
And declare a variable headerComponent in the .ts:
public headerComponent = MyCoolHeaderComponent;
You will have to provide your own UI and functionality for the header (in your MyCoolHeaderComponent class).
MatDatepicker component uses DateAdapter service to handle dates, and when you click on next or previous button, a handler runs on MatCalendarHeader component and this handler calls addCalendarMonths method on DateAdapter (if current view is month) so if you patch this function on DateAdapter service TEMPORARILY, you can do anything you like, for example:
in parent component
origAddCalendarMonths: any
constructor(#Optional() private _dateAdapter: DateAdapter<any>) { }
onOpenedStream() {
this.origAddCalendarMonths = this._dateAdapter.addCalendarMonths
this._dateAdapter.addCalendarMonths = (...args) => {
console.log('!!!')
// your logic
return this.origAddCalendarMonths.apply(this._dateAdapter, args)
}
}
onClosedStream() {
this._dateAdapter.addCalendarMonths = this.origAddCalendarMonths
}
ngOnDestroy() {
this.onClosedStream()
}
or if you use MatCalendar instead, you can provide some extended class from DateAdapter in parent component providers to provide that class as DateAdapter for all child of this component (NodeInjector)
I am trying to create my first Xamarin.Forms custom user control named LocationSelector. It has an Entry and when the user enters something, a list with selectable locations is shown (similary to the selection in Google Maps).
The selected location is the important 'return value' of the control.
My plan is to catch the ItemSelected event of the list and set the SelectedLocation property. LocationSelector is designed as MVVM and since everything is working so far here just the Code-Behind (which I think is enough to describe the problem):
public partial class LocationSelector : StackLayout
{
public static readonly BindableProperty SelectedLocationProperty =
BindableProperty.Create<LocationSelector, LocationModel>(s => s.SelectedLocation, new LocationModel(), BindingMode.TwoWay);
public LocationSelector()
{
InitializeComponent();
var model = new LocationSelectorModel();
BindingContext = model;
_listView.ItemSelected += (sender, args) =>
{
SelectedLocation = model.SelectedLocation;
};
}
public LocationModel SelectedLocation
{
get { return (LocationModel)GetValue(SelectedLocationProperty); }
set { SetValue(SelectedLocationProperty, value); }
}
}
Now I want to use this control on a search view where the BindingContext is set to the SearchViewModel:
<ContentPage x:Class="Application.App.Views.SearchView" ...>
<c:LocationSelector SelectedLocation="{Binding Location}"/>
</ContentPage>
public class SearchViewModel : ViewModel
{
private LocationModel _location;
public LocationModel Location
{
get { return _location; }
set { SetProperty(ref _location, value); }
}
}
Unfortunately this is not working. The output throws a binding warning:
Binding: 'Location' property not found on 'Application.App.CustomControls.LocationSelectorModel', target property: 'Application.App.CustomControls.LocationSelector.SelectedLocation'
Why points the binding to a property in the ViewModel that is used 'within' my custom control and not to the property in the BindingContext of the view?
The problem is setting the BindingContext to the view model of the user control:
public LocationSelector()
{
var model = new LocationSelectorModel();
BindingContext = model; // this causes the problem
// ...
}
In this post I found the solution. Setting the BindingContext to each child elements rather than the whole user control is doing the job:
public LocationSelector()
{
var model = new LocationSelectorModel();
foreach (var child in Children)
{
child.BindingContext = model;
}
}
This is my model:
var AppModel = Backbone.Model.extend({
defaults : {
xmlDeclarationAndDoctype : ''
},
renderFoobar : function () {
this.set({'xmlDeclarationAndDoctype' : 'foobar'});
this.fetchFoobar();
},
fetchFoobar : function () {
console.log(this.get('xmlDeclarationAndDoctype'));
},
fetchAgain : function () {
console.log(this.get('xmlDeclarationAndDoctype'));
}
});
My View:
window.AppView = Backbone.View.extend({
initialize : function () {
model = new AppModel({});
},
render : function () {
model.renderFoobar();
}
});
When I call the following code in my HTML page, I get 'foobar' once the page is rendered.
$(window).load(function () {
var appView = new AppView;
});
In that page, after clicking a button, I'm calling a function which has the following code but this time I get an empty string instead of 'foobar'.
model = new AppModel({});
model.fetchAgain();
Whats wrong in above code?
UPDATES:
mu is too short has given the valid answer, Thanks a ton!. I just would like to add the excerpt out of the correct answer given below.
The new AppModel({}) in AppView#initialize is not the same as the new AppModel({}) somewhere else.
Demo : http://jsfiddle.net/sjagf/2/
The new AppModel({}) that you create in AppView#initialize is not the same as the new AppModel({}) that you create in your button's handler. You're looking at the xmlDeclarationAndDoctype property of two different models and getting two different results.
Calling new AppModel(o), for some set of options o, twice doesn't give you the same object. Backbone doesn't keep track of all the objects that it has created, that's your job or your collection's job. If you look at the cid of your models you'll see:
http://jsfiddle.net/ambiguous/sjagf/
You probably want a collection to help you keep track of your models.
You have more problems. Your view is using a global variable, model:
window.AppView = Backbone.View.extend({
initialize : function () {
model = new AppModel({}); // This is a global
},
and that's probably getting overwritten in your button handler. You should attach model to this:
window.AppView = Backbone.View.extend({
initialize : function () {
this.model = new AppModel({});
},
render : function () {
this.model.renderFoobar();
}
});
Or perhaps like this:
window.AppView = Backbone.View.extend({
render : function () {
this.model.renderFoobar();
}
});
// And elsewhere...
var v = AppView.new({ model: new AppModel({}) });