Unfortunately, your application stopped working - view

In Alloy framework, I want to dynamically add a view defined in an xml file but not bound to any other view to another.
Let's take the following example:
Container I want to fill in index.xml:
<ScrollableView id="scrollableBilan" showPagingControl="true">
</ScrollableView>
The view template question.xml I want to instanciate for each view going into the ScrollableView:
<Alloy>
<Collection src="ReponsePossible">
<View id="questionContainer" class="container">
<Label id="questionText" />
<Button id="buttonNextQuestion">Question suivante</Button>
</View>
</Alloy>
Finally the controller index.js, question being a Collection instance:
for(var i=0; i<questions.length; i++){
$.scrollableBilan.add(Alloy.createController('question', questions.at(i)));
}
This makes my application crash whith following message: 'Unfortunately, your application has stopped'. I already got this error, always when trying to add a view dynamically create using Alloy.createController.
The behavior is ok when creating a view with Ti.UI.createView but I want to use the MVC...
Any help is welcomed!

You are passing it a model object, instead try passing it a JSON object like this:
for(var i=0; i<questions.length; i++){
$.scrollableBilan.add(Alloy.createController('question', questions.at(i).toJSON()));
}
Alternatively you can just do this all inside the xml file by using Data Binding and the dataCollection attribute, put something like this in your index.xml:
<Alloy>
<Collection src="questions">
<ScrollableView id="scrollableBilan" showPagingControl="true" dataCollection="ReponsePossible">
<View id="questionContainer" class="container">
<Label id="questionText" text="{questionText}"/>
<Button id="buttonNextQuestion">Question suivante</Button>
</View>
</ScrollableView>
</Alloy>
The questionText field needs to be an attributes in your questions model.

Related

antd Table is not automatically rerendered when datasource data changes

Ant Design Table is not rerendered automatically when datasource data changes.
<Table
columns={columns}
dataSource={filteredData}
pagination={pagination}
loading={loading}
onChange={this.handleChange} />
filteredData is changed in a method based on a custom filter which is placed outside the table.
Shouldn't the table rerender automatically when filteredData is changed?
Does anybody know how to refresh the table when filteredData is changed?
If you want a Table to re-render automatically, filteredData should be state.
onSourceChange = (something) => {
this.setState({filteredData: something})
}
render(){
return (
<div>
<Table
columns={columns}
dataSource={this.state.filteredData}
pagination={pagination}
loading={loading}
onChange={this.handleChange} />
<button onClick={()=>onSourceChange(['a','b','c'])}>change datasource</button>
</div>
)}
I can imagine that people are still looking for answers specially with the newest version of antd.
So in Antd version 5.x table API, you could find a property call rowKey.
In version 4.0 table API, the property called key thou.
And to way to tackle the problem correctly is to set it like following:
<Table
columns={columns}
dataSource={this.state.filteredData}
rowKey={this.state.filteredData.key}
pagination={pagination}
loading={loading}
onChange={this.handleChange} />
Note: Please consider to have a key property in your array of course. key must be unique.

How to pass ViewModels into Razor Components in .NET Core 3.1

I have a View MyView.cshtml with the following content:
#using MyProject.ViewModels
#model MyProject.ViewModels.MyViewViewModel
<form asp-action="Test" method="Post">
<component type="typeof(MyProject.Views.Home.Test)" render-mode="ServerPrerendered" />
<input type="submit" value="send"/>
</form>
And I have the Razor Component Test.razor with the following content (with Blazor Syntax):
#page "/Test"
<div class="form-group top-buffer #Visible">
<div class="row">
<div class="col-2">
<label asp-for="TestName" class="control-label"></label>
</div>
<div class="col-3">
<input asp-for="TestName" class="form-control" />
<span asp-validation-for="TestName" class="text-danger"></span>
</div>
</div>
</div>
<button #onclick="Show">Show</button>
#code {
public string Visible { get; set; } = "hidden";
protected async Task Show()
{
Visible = "";
}
}
The Class MyViewViewModel would look like this:
namespace MyProject.ViewModels
{
public class MyViewViewModel
{
[Display(Name = "Test Name:")]
public string TestName { get; set; }
}
}
Works all pretty fine so far. However I now want to use this component as part of a Web form which will be sent to the controller after submission. That's why I need to access and change properties of my ViewModel 'MyViewViewModel'. Unfortunately I did not find any answer in the internet on how to do that. I can't use #model MyProject.ViewModels.MyViewViewModel like in the view because this will give me a compilation error. I wonder if I need to use #inject, but if yes, I don't know how...
(parts are used from this example: https://jonhilton.net/use-blazor-in-existing-app/)
When you mix Blazor in a Razor Page, you can do the following:
Render a Razor Component
Interact with a Razor Component
Pass a Razor Component values
Please keep in mind that you are dealing with two different life-cycles. So if you do work inside of a Razor Component, the component will update but not effect the Razor Page it is hosted inside of. So mixing Razor Components and Pages with forms would be difficult.
More specifically to the OP. To pass data from your ViewModel to the component you may use the following method.
#using MyProject.ViewModels
#model MyProject.ViewModels.MyViewViewModel
<form asp-action="Test" method="Post">
<component type="typeof(MyProject.Views.Home.Test)"
render-mode="ServerPrerendered"
param-Name="#Model.TestName"/>
<input type="submit" value="send"/>
</form>
Test.razor
<h3>HelloWorld</h3>
Hello #Name
#code {
[Parameter]
public string Name { get; set; } = "undefined";
}
About life cycles
Basically when you have a button in Blazor, it will trigger an event which causes the component to re-render. You could imagine it like an iframe, or update-panel. When you have a button in a Razor page, it does a HTTP call round trip and reloads the page entirely. There is no event system in place to tell Blazor to invoke an HTTP call round trip to refresh the Razor page's content and vise versa. You can only one-way data-bind from Razor pages to Blazor, think write-only, and only when the page loads.
To hopefully add to the info. With a ASP.Net Core MVC project host Blazor webassembly, I was trying to pass a viewmodel into a razor component using this code in my view cshtml file:
<component Type="typeof(Leave)" render-mode="WebAssembly" model="new { model = (MyViewModel)#Model})"/>
But it would fail to render the razor component if I tried to access data in the viewmodel from the razor component with an Object not set exception. I think it was accessing the data before the view model has been initialized. Maybe if I set a default value this could avoided?
I found by using this instead I was able to get it working.
#(await Html.RenderComponentAsync<Leave>(RenderMode.WebAssembly,new { model = (MyViewModel)#Model}))
Edit
Seems you also need to register the viewModel class in the services in the Blazor WASM project in the Program.cs file.
builder.Services.AddScoped(sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<MyViewModel,MyViewModel>(); // <= add this line
await builder.Build().RunAsync();`
Without that I would get an error saying the property could not be found.
Hopefully this saves someone else some time :-)

kendo MVVM data-bind dropdownlist

How can I bind a local array to the MVVM dropdownlist of kendo.
I have an array like this
var array = [0.0, 20.00]
and I want to bind it to my input control
<input data-role="dropdownlist"
data-bind='"source: ' + array + '"' />
Its not working. Any ideas how I can achieve this?
thanks
The MVVM source binding accepts model field, and not a variable in the window scope. If you would like just bind the DropDownList to primitive values, then use the data-source attribute:
<input data-role="dropdownlist" data-source="array" />
Here is a runnable demo.
If you would like to use the source binding, then define a view model. Here is another demo that demonstrates this approach.
It's hard to tell from your question whether you have forgotten to use kendo.bind() to bind the View to the ViewModel but I suggest you also review the Kendo UI Framework Source Binding documentation for the syntax of data-bind. Also check the DropDownList MVVM Demo for a more complete example. A minimalist implementation is shown below:
<body id="appRoot">
<p>Minimalist DropDownList example</p>
<input data-role="dropdownlist" data-bind="source: array">
<script>
// Ideally you would use this viewModel variable instead of the plain JavaScript object literal below
var viewModel = kendo.observable( { array: [ 0.0, 20.00 ] } );
kendo.bind($("#appRoot"), { array: [ 0.0, 20.00 ] } );
</script>
</body>

Can one avoid multiple DOM elements with same id, when using Backbone/Marionette view instances?

When I create multiple view instances of the Marionette view which is linked with a template html with ids, these would get duplicated for multiple instances of these views.
While it works correctly, I feel that there ought to be more architecturally correct way of doing this.
The example code is like below.
Template:
<script id="myTemplate" type="text/template">
<div id="myDiv">
<input type="text" id="myText"/>
<input type="button" id="myBtn" value="Click me!"/>
</div>
</script>
View:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
var v1= new MyView();
v1.render();
var v2= new MyView();
v2.render(); //Duplicate IDs now present in DOM
I need some unique identification of these DOM elements and hence the ids.
Even when tying the model to this view, we need some way to identify these DOM elements.
What is the correct way of doing this without duplicating the ids.
Just pass the id to the view when you create it:
Template:
<script id="myTemplate" type="text/template">
<input type="text" class="js-myText"/>
<input type="button" class="js-myBtn" value="Click me!"/>
</script>
View def:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
Instanciation:
var v1= new MyView({ id: "view" + number});
v1.render();
Then you can provide dynamic id values for your views (e.g. by using a model id).
That said, when using Marionette you shouldn't need to call render: you should instead show a view within a region. Take a look at the free sample to my Marionette book to get you up to speed.
If you must go for unique IDs to make sure no one accidentally duplicates a class name inside a view, you can use:
Underscore's uniqueId method to generate a unique ID for each DOM element inside the view, like: <input type="text" id= <%= _.uniqueId("myText_") %> /> This will just make sure that IDs are not duplicated. But they're not very helpful if you need to identify the elements by these IDs.
Marionette's TemplateHelpers which allow you to use helper functions from inside the templates:
//Define this inside your view:
templateHelpers: function() {
var that = this;
return {
getIdSuffix : function() { return that.idSuffix; }
/*Where idSuffix is passed to the view during instantiation
and assigned to this.idSuffix */
};
}
//In the template:
<input type="text" id= <%= "myText_" + getIdSuffix() %> />
You now know before runtime what DOM IDs you will have, provided care is taken not to give the same idSuffix to more than one view instance.
Simply put, don't use an id if it's not unique. Use a class or some other way of identifying the element.
You can use any jQuery selector to locate the element you want, ranging from the insane and brittle:
this.$('div > input:first'); // don't actually do this!
to the slower but semantically better:
this.$('[data-element-type="some-text-box-descriptive-name"]');
Although in reality, using a class is best, because that's what a class is for - for identifying a type of element. I can see that a maintainer might not know not to change your class in the template, so a data-attribute might be acceptable, or maybe even (in this case):
this.$('input[type=text]');

How do you add fields to com_content in Joomla! with a plugin and store the data in its own table?

I'm running Joomla 1.7 and I know that it has the ability to add custom form fields to components with a plugin.
There is a sample plugin located at:
/plugins/user/profile
This plugin allows you to put custom form fields on the user profile front end and back end and these fields are stored in a custom table.
I created a similar plugin for user profiles and it worked perfectly.
However, when I go to create a plugin like this for com_content, I am met with a problem.
this is what my XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<form>
<fields name="additionalinfo">
<fieldset name="additionalinfo_fieldset" label="PLG_CONTENT_ADDITIONALINFO_FIELDSET_LABEL">
<field name="tagline" type="text" size="50" default="" label="PLG_CONTENT_ADDITIONALINFO_TAGLINE_LABEL" description="PLG_CONTENT_ADDITIONALINFO_TAGLINE_DESC" />
<field name="pseudocategory" type="category" extension="com_content" label="PLG_CONTENT_ADDITIONALINFO_PSEUDOCATEGORY_FIELD_LABEL" description="PLG_CONTENT_ADDITIONALINFO_PSEUDOCATEGORY_FIELD_DESC" />
</fieldset>
</fields>
</form>
This however does not work, whenever I do something like above, the form fields never show up on the admin form (even though I have it set correctly, and the only thing that changed between the user plugin and the content plugin is the name of the form i'd like the form to appear on
When I change my XML to this:
<?xml version="1.0" encoding="utf-8"?>
<form>
<fields name="attribs">
<fieldset name="additionalinfo_fieldset" label="PLG_CONTENT_ADDITIONALINFO_FIELDSET_LABEL">
<field name="tagline" type="text" size="50" default="" label="PLG_CONTENT_ADDITIONALINFO_TAGLINE_LABEL" description="PLG_CONTENT_ADDITIONALINFO_TAGLINE_DESC" />
<field name="pseudocategory" type="category" extension="com_content" label="PLG_CONTENT_ADDITIONALINFO_PSEUDOCATEGORY_FIELD_LABEL" description="PLG_CONTENT_ADDITIONALINFO_PSEUDOCATEGORY_FIELD_DESC" />
</fieldset>
</fields>
</form>
When I make this simple change, the form fields show up! BUT, the data isn't stored or retrieved from the custom table, it just goes into the 'attribs' column on the _content table. This stores the content in JSON, which is alright, but we'd like to be able to index the content by the custom fields (and not have to loop through each record in the database to find what we're looking for).
Any ideas on how to fix this?
thanks!
david barratt
I guess your plugin file ( for example, "yourplugin.php" ) will have one method called "onContentPrepareForm". If you want to add data to an article, this method should start like this:
function onContentPrepareForm($form, $data)
{
if (!($form instanceof JForm))
{
$this->_subject->setError('JERROR_NOT_A_FORM');
return false;
}
// Check we're manipulating an
if ( $form->getName() != "com_content.article" ) {
return true;
}
//[...] The rest of your code here
Besides, if you want to store these fields in another table in order to make it easier to search using this fields, maybe you should create a new table and save the data using the "onContentAfterSave" method:
public function onContentAfterSave( $context, &$article, $isNew )
On this method, you should always check that $context is "com_content.article", otherwise you might face problems when saving categories.
I hope it helps!

Resources