External access to view elements created with createController and added to another view - appcelerator

I have a problem.
There is a view row.xml
<Alloy>
<View>
<Switch id = "search_switch" bindId = "search_switch" />
<Label id = "search_switch_label" />
</ View>
</Alloy>
And MAIN view
<View id = "general" bindId = "general ">
<Label id = "testLabel" />
</View>
When I adding
var control = Alloy.createController ('row');
Control.search_switch_label.text = 'TEST';
$.parent.add (control.getView());
All is perfectly added, but how to access the methods and properties of search_switch?
$.search_switch - says that "undefined"!!!
$.testLabel.text = '1234567890'; - All OK!

In your main view if you are trying to get the methods of row.xml, then you have two basic ways to do that :
1) Using the exports (in row.js)
var init = function (){
// some code here
}
exports.getInit = init;
2) Using the Alloy.Globals (in row.js)
Alloy.Globals.init = function(){
// some code here
}
Now to access the init function in Main View, you'll have to do the following respectively :
1) access exports function (in MainView.js)
var control = Alloy.createController ('row');
control.getInit();
2) access Alloy.Globals function (in MainView.js)
var control = Alloy.createController ('row');
if(Alloy.Globals.init)
Alloy.Globals.init();
Good Luck & Cheers

Firs thing as per your code is that when you use $, it means you are accessing the controls of the current controller so you can not use other controller's controls just by using $ sign. In your case, you need to set reference in you row.js file so you can access that controls or any variable or function in other controllers.
You need to do same as you did for search_switch_label. You can access search_switch object by control object in main.js like
control.search_switch.value

Related

Toggle Class Prototype Function

I'm trying to iterate over an array of elements, specified by class name. Then I want to use a function to toggle the class and change some text.
This does NOT work:
$$('.btn').forEach( setButtonLoadingStateOn, this );
setButtonLoadingStateOn = function( btn ) {
btn.toggleClassName('loading');
btn.disable();
btn.select('span span')[0].update( "please wait..." );
}
This does NOT work:
$$('.btn').each( function(btn) {
btn.addClassName('loading');
btn.disable();
btn.select('span span')[0].innerHTML = "please wait...";
});
This also does NOT work:
setButtonLoadingState( '.btn', 'start' );
setButtonLoadingState = function( btnClass, loadState ) {
btnElem = $$( btnClass );
btnElem.each(function( el ){
if ( loadState == 'start' ) {
el.addClassName( 'loading' );
el.disable();
el.select('span span')[0].innerHTML = "please wait...";
} else {
el.removeClassName( 'loading' );
el.enable();
el.select('span span')[0].innerHTML = "buy now";
}
});
}
If I console.log() the element, I get the object (or array) I'm expecting, so I don't know what's wrong.
I've also tried several SO answers, including this one: Add or remove class with prototype
In case it matters, the platform is Magento CE 1.8.0.2. How can I update these button elements using Prototype? Thanks.
~ edit ~
There are several HTML elements on the page. They all look like this:
<button type="button" title="buy now" class="button btn" onclick="productAddToCart(this)">
<span><span>Quick Buy</span></span>
</button>
Thanks so much to all who contributed. The AJAX request was returning a hyphenated key in the JSON response:
{"msg-html":"blah blah blah"}
Reading this from the response object as follows was the problem:
var ajaxResult = transport.responseText.evalJSON();
var ajaxMsg = ajaxResult.msg-html;
Hyphens are not allowed in JSON keys, instead the object needs to be accessed as follows:
var ajaxMsg = ajaxResult["msg-html"];
... or remove the hyphens! Thanks again to all.
Full credit to this dude/dudette: http://developer.appcelerator.com/question/120227/parsing-json-with-hyphenated-key-names

How do I tell AngularJS that I've been cheating on it with jQuery?

I'm hacking together an admin interface using a pre-fab template. The navigation system that is already built uses jQuery to load content into a tag via AJAX based on window.location.hash.
If I fetch this snipped via an AJAX call and then insert it into the DOM:
<div ng-app="myApp">
{{ 2 + 2 }}
</div>
AngularJS has no idea that I've updated the DOM. The content that I see is, literally, {{ 2 + 2 }}. If I reload the page, the expression evaluates and I see the value 4 that I desire.
How do I ask AngularJS to please evaluate the content that I so brutally forced upon the DOM via non-Angularian methods?
Here's a fiddle: http://jsfiddle.net/4zRTH/
EDIT
The answer from #tasseKATT works great for simple expressions, but I'm having trouble accessing a controller. I have a NotesController that the code that I'm importing needs to access. Something more along the lines of:
<div ng-controller="NotesController">
{{ notes.length }}
</div>
I updated the fiddle so it's a little more along the lines of what I'm looking to do: http://jsfiddle.net/5Xhs9/4/
You need to use $apply:
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life cycle of
exception handling, executing watches.
In your example however the app is not even bootstrapped, so you need to:
1) Select the element:
var dynamicContent = "<div id='someId' ng-app> {{ 2 + 2 }} </div>";
$('.expression').html(dynamicContent);
var element = angular.element(document.querySelector('#someId'));
2) Bootstrap it:
angular.bootstrap(element, []);
3) Retrieve the scope:
var elementScope = element.scope();
4) Call $apply:
elementScope.$apply();
Demo: http://jsfiddle.net/5Xhs9/
Edit for new scenario:
In your new example you have added ng-app"myApp" to an existing div, so AngularJS will bootstrap your application for you, and now you want to dynamically add this content instead:
<div id='notes' ng-controller='NotesController'> {{ notes.length }} </div>
You have already defined the NotesController and added it to the module. What you now can do is:
1) Retrieve the $injector service:
var $injector = angular.element(document.querySelector('.container')).injector();
2) Use the $injector to compile the newly added element and link it to its scope. Call $apply:
var element = angular.element(document.querySelector('#notes'));
$injector.invoke(function($compile) {
var scope = element.scope();
$compile(element)(scope);
scope.$apply();
});
Demo: http://plnkr.co/edit/P6VwLee6AUWO7aDT601m?p=preview
You can cheat by saving $compile and $rootScope in the app.run function on window: http://plnkr.co/edit/pxGhof1zD0rZknagfmyc?p=preview
window.compileForAngular = null;
window.rootScope = null;
var app = angular.module('jQuery', []);
app.run(function ($compile, $rootScope) {
window.rootScope = $rootScope;
window.compileForAngular = $compile;
doNotTryThisAtHome();
});
function doNotTryThisAtHome() {
// let's just pretend that this came in over an AJAX request
var dynamicContent = "<div> {{ 2 + 2 }} </div>";
// I insert it into the DOM via jQuery
$('.expression').html(window.compileForAngular(dynamicContent)(window.rootScope));
}
http://jsfiddle.net/QC97L/2/
See the above fiddle
// Code goes here
jQuery(document).ready(function () {
// let's just pretend that this came in over an AJAX request
var dynamicContent = "<div id='mainApp' > {{ 2 + 2 }} </div>";
// I insert it into the DOM via jQuery
$('.expression').html(dynamicContent);
var app = angular.module('myApp',[])
angular.bootstrap('#mainApp',['myApp']);
// now how do I get AngularJS to evalulate the expression?
});

Bind model to view from controller with alloy appcelerator

EDIT: Heres the example: https://github.com/prakash-anubavam/alloypreloadedsqlitedb/tree/master/app
And sumarizing, how does the view details.xml know which model is it taking the data from?
Coming from asp.net mvc, im having some issues understanding this MVC.
I can understand how to use tables and such on the view like:
<TableView id="table" dataCollection="fighters" onClick="showId" dataTransform="transformData">
And fetch the data in the controller, i know it will use the global (singleton) collection of fighters and that will bind the model to the view.
But i have come across an example (i cant really find now) where it had a View, with no table, just some labels and text='{variableName}', which i assume it gets from the model.
However the controller, did not assign the model (coming from an args[0] because it was always called from another controller which had the actual table), but it never assigned the model instance to the view in any way... so the question is how did it work? Is alloy smart enough to detect the actual model instance and use it? How would i do it? Something like $model = ...; or $.details.model = ...; or something like that? How did the view know where to take '{variableName}' from if the model was never assigned with a table or something.
This is actually a carryover hack, that may not work in the future, according to this thread.
If you take a look at index.js in your example (the controller), the model is assigned by the onClick event of the TableView:
function showId(e) {
if (e.row.model) {
var detailObj=fighters.get(e.row.model);
// Assigning to the $model actually sets this model as the one to bind too
var win=Alloy.createController('detail',{"$model":detailObj});
win.getView().open();
}
}
This is a "specai" variable that is automagically assigned for databinding, and is how it works underneath the covers (or did work under the covers).
This is undocumented and NOT ideal or recommended.
I've found Tony Lukasavage answer the cleaner aproach to bind a existing model to view:
You can find it here Josiah Hester answer is based on it (yeah, beware it's kind of a hack).
Although, Fokke Zandbergen gave an alternative worth looking at, maybe less hackish, don't know.
Expanding Josiah answer, you could do as follows:
On Master view:
<Alloy>
<Collection src="modelName" />
<View id="topview" class="container">
<TableView id="tblModels" dataCollection="modelName" dataTransform="transformModel">
<Require src="rowModel"/>
</TableView>
</View>
</Alloy>
Then, on master controller:
//retrieve the id of the model
var thisId = e.row.thisIndex;
//pass special key $model
var detailController = Alloy.createController("detail", { "$model": Alloy.Collections.detail.get(thisId) });
detailController.getView().open();
Then, on detail view:
<Alloy>
<View class="container" >
<Label text="{id}"/>
<Label text="{fullName}"/>
</View>
</Alloy>
On detail controller: do nothing special.
If you have a transform function on master controller, it returns a object and you can use its properties inside detail view like "{fullName}".

Get the ID of a custom Control

I have a fairly complex Custom Control that may be used multiple times on any given XPage. In the control I create a couple of viewScope variables that have to be unique to the specific custom Control. I would like to do something like viewScope.put(customControlID + "variableName","Stuff)
But I don't know how to get the custom controls ID
You can get the current custom control ID with this.getId() at the <xp:view> level.
If you put this ID into a compositeData variable (e.g. compositeData.id) then you can use the ID inside the custom control everywhere you want.
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
beforePageLoad="#{javascript:compositeData.id = this.getId()}" >
Usage in SSJS:
viewScope.put(compositeData.id + "variableName","Stuff")
Typically, IDs are named like "_id2", "_id8", ...
You can use a dataContext variable:
<xp:this.dataContexts>
<xp:dataContext
value="#{javascript:this.getId()}"
var="id">
</xp:dataContext>
</xp:this.dataContexts>
The variable is then accessible as id in SSJS...
<xp:label id="label1" value="#{javascript:id}" />
... or in EL:
<xp:label id="label1" value="#{id}" />
Here is another solution as an SSJS function:
function getCCId( cmp:javax.faces.component.UIComponent):string{
try{
if( typeof( cmp ) === 'com.ibm.xsp.component.UIIncludeComposite' ){
return cmp.getId();
}
return getCCId( cmp.getParent() )
}catch(e){}
}
The function climbs the component tree until it finds the parent CC and then returns the id.
You can use it f.e. in a label like this:
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:getCCId( this )}]]></xp:this.value>
</xp:label>

Zend Form: onchange select load another view content

In my application I have a form in controller/index that consists out of 3 select boxes. When all three boxes have a value selected I need to show additional html and extra form options in the same view based on those select values. The obvious solution seems to make an ajax call to another action that handles the database operation and creates a view and loading that view into the controller/index.phtml
I have been able to load a view of another action in the index.phtml by using:
$('#select').change(function() {
event.preventDefault();
var id = $(this).attr('id');
$('#results').show();
$('#results').load('/controller/index/' + $(this).attr('value'));
return false;
});
However I need to pass the variables of all three select boxes and for that I alternatively used:
$('#select1').change(function() {
var select1 = $('#select1').val();
var select2 = $('#select2').val();
var select3 = $('#select3').val();
$.ajax({
type: 'POST',
dataType: 'json',
url: '/controller/index/',
data: { select1: select1, select2: select2, select3: select3},
success: function(result){
var return1 = result.return1;
var return2 = result.return2;
}
});
});
The last method works in as far that I do see the variables passed in the headers and the response contains the view, but I cant fix it that just the content of the ajax view is placed within the index view. (Ofcourse by not using AjaxContent switching, the ajax view will load but that includes the complete layout as well.) Anything that I echo in the ajax action or ajax view do not show in the index view. Any pointer would be more than welcome
EDIT
the ajax action now looks like
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$select1 = $this->_request->getParam('select1');
$select2 = $this->_request->getParam('select2');
$select3 = $this->_request->getParam('select3');
// DO THE OTHER STUFF AND LOGIC HERE
$results = array(
'return1' => 'value1',
'return2' => 'value2'
);
$this->_response->setBody(json_encode($results));
and the controller init
public function init() {
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('ajax', 'json')->initContext();
}
So everything works, I can see the returned values in the response by using developer tool (network) in my browser, however I just do not know how I can use this to "update" the view
You can do two things:
You can enable the layout of the action you are calling via ajax. See you have disabled layout so even if the view phtml file of the ajax action contains something, it won't show. You can enable layout, use text/html dataType instead of json and show the returned HTML somewhere.
Or, in the success event of the ajax call, write javascript codes to update DOM.
Thanks #Salman for your suggestions as they lead me in the right direction and I managed to solve the problem.
I managed to pass multiple parameters with the ajax .load() call by passing them as get parameters.
The results of the ajaxAction could then be formatted in the ajax.ajax.phtml view and were consecutively
shown within the #results div that resides in the index.phtml where the select boxes are.
controller/index.phtml
<div id="results" style="display:block;">Select all three values</div>
IndexController init and ajaxAction
public function init() {
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('ajax', 'html')->initContext('html');
}
public function ajaxAction() {
$select1 = $this->_request->getQuery('select1');
$select2 = $this->_request->getQuery('select2');
$select3 = $this->_request->getQuery('select3');
$form = new Application_Form();
// Database operations and logic
$this->view->form = $form;
$this->view->array = $somearray;
}
}
jquery script in index.phtml
$(document).ready(function(){
$('.selector').change(function() {
var select1 = $('#select1').val();
var select2 = $('#select2').val();
var select3 = $('#select3').val();
if ( select1 && select2 && select3) {
$('#results').show();
$('#results').load('/controller/ajax?select1=' + select1 + '&select2=' + select2 + '&select3=' + select3);
}
});
});
controller/ajax.ajax.phtml
<?php if ( $this->array ) : ?>
<?php echo( $this->form ); ?>
<?php else: ?>
Nothing found for selected values
<?php endif ?>

Resources