How to make a model 'dirty' in angularjs? - angularjs

I am working on a markdown editor.
<textarea class="wnd-content-textarea"
name="" id="" cols="30" rows="10"
ng-model="content"
ng-change="">
</textarea>
I use a jquery plugin to update the a model that is binded on textarea element:
scope.execEditorCmd = function (cmd) {
// List of commands:
// 'cmd-bold',
// 'cmd-italic',
// 'cmd-quote',
// 'cmd-unorder-list',
// 'cmd-order-list'
/**
* Get selected text in textarea tag.
* textrange() is dependent on jquery-textrange plugin
* #type {Object}
* e.g.: {position: 4, start: 4, end: 8, length: 4, text: "reqw"}
*/
var selectedTextObj = $textAreaElement.textrange('get');
// Execute the string replace command using structure defined before
var boldedText = selectedTextObj.text
.replace( markdownCmd[cmd].search , markdownCmd[cmd].replace);
// Actually replace the orginal text with executed string in textarea.
// $timeout();
$textAreaElement.textrange('replace', boldedText);
// Not sure if it is the right way, but I have to do it to update model.
scope.content = $textAreaElement.val();
};
The jquery plguin is here:
The text on in the textarea changed in browser, but the model actually still remains the same. I checked it by {{}} outputing in in browser and log it in console.
At this moment, if I type a space in textarea, then all the changes that this plugin had done will reflected in model.
I tried scope.$apply, which result in console error.
I also tried $timeout(function(/* jquery updating */){}), nothing changes.
How do I make the change reflected on model?
- trigger it dirty?
- any best practice?
Thank you!

Related

How to get immediate text with webdriverIO

I have html DOM like this:
<div class="alert alert-danger">
<button class="close" aria-hidden="true" data-dismiss="alert" type="button">×</button>
Your username or password was incorrect.
</div>
I would like to get Your username or password was incorrect. text.
If I do:
$('.global-alerts div.alert-danger').getText()
Then I get this ×.
Is there a way to get the only text part inside that div element?
I managed to do something like this:
getErrorMessageText() {
return browser.execute(function () {
const selector = '.global-alerts div.alert-danger';
// #ts-ignore
return $(selector).contents().not($(selector).children()).text().trim();
});
}
And it works.
But does anybody have better idea? Or more of like webdriverIO approach here?
Does it work if you use something like this?
var innerHTML = $('.global-alerts div.alert-danger').getHTML(false);
the false argument tells indicates whether or not to include the selector element tag in the output.
Serious solution
I do not quite see any other way but to use execute in order to "grab" that information from the page.
I would however place it in a browser command (either do it in the config "before" hook, or add a service that adds the command in the before hook)
This is what I ended up with considering typescript as main language, ignoring the use of jQuery, and considering that you use the before hook:
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* #param {Array.<Object>} capabilities list of capabilities details
* #param {Array.<String>} specs List of spec file paths that are to be run
* #param {Object} browser instance of created browser/device session
*/
before: function (_capabilities: any, _specs: any, browser: WebdriverIO.Browser) {
browser.addCommand(
'getNodeText',
async function() {
return this.execute(
(selector: string) =>
Array.from( document.querySelector(selector).childNodes || [])
.filter((n: HTMLElement) => n.nodeType === n.TEXT_NODE)
.map(n => n.textContent)
.reduce(function(f, c) {return f+c;}, '')
.replace('\n', '')
.trim(),
this.selector
);
},
true
);
},
With this approach, typescript might complain about the function that passed to webdriver to get executed, so you can either write it properly, or just move it to a .js file and be done with it.
Just watch for document.querySelector(selector), in theory, it should not be null since the command is executed on an already found by webdriver element.
The way you grab the text there is just await (await $('.alert.alert-danger').getNodeText());
This should return the full string from within the node itself, but not any subchild.
Note: If you end up with an element like: <div id="mytxt">my text style is <strong>strong</strong> and <italic> italic </italic>. - html fan</div> and you do this getNodeText(), you probably end up with the value my text style is and . - html fan.
The "don't get bothered to much" solution
This approach will also sort of check that the "x" button is still there.
await expect($('.global-alerts div.alert-danger')).toHaveText('xYour username or password was incorrect.')
in latest version of WebDriverIO (v8), you can use this selector: aria/YourContent. For example:
With the DOM like this:
<h1>Hello World!</h1>
You can use this selector
console.log(await $('aria/Hello World!').getText()) // outputs: "Hello World!"
Ref: https://webdriver.io/docs/selectors/#fetch-by-content

angularjs calling select on corresponding HTMLinput doesn't highlight cell contents if value comes from ng-model?

I am trying to select (highlight) the text in an input element whose value comes from a model, but it doesn't seem to work.
The element (part of component template) in question is as follows:
<input id="table-input-cell" ng-blur="$ctrl.inputBlur()"
ng-focus="$ctrl.inputFocus($event)" ng-model="$ctrl.activeText"
class="input-cell"/>
The controller function looks like this:
ctrl.inputFocus = function(focusEvent) {
let el = ctrl.inputView[0];
console.log(el.constructor.name);
console.log(`inputFocus called. contents: ${el.value} `);
el.select();
};
What I am seeing as the result on the page is this:
What I would like to see is this:
Here's the console output:
HTMLInputElement
budget_controller.source.js:261 inputFocus called. contents:
So it looks like the problem is that the ng-focus may be called before the value of the input box is populated by ng-model, which is why .select() isn't highlight anything.
Or maybe it something else? Anyway, is there some other life-cycle event in AngularJs I can use to set the selection range once the element is bound as has focus? I don't see any thing ngBound= in the dox.
Use the event target:
ctrl.inputFocus = function(focusEvent) {
̶l̶e̶t̶ ̶e̶l̶ ̶=̶ ̶c̶t̶r̶l̶.̶i̶n̶p̶u̶t̶V̶i̶e̶w̶[̶0̶]̶;̶
let el = focusEvent.target;
console.log(el.constructor.name);
console.log(`inputFocus called. contents: ${el.value} `);
el.select();
};
The DEMO on PLNKR

ng-tags-input not working correctly with autocomplete

I'm adding tag by selecting from list (which is populated using $http request). The tag is added but the text which I have typed that remains there with ng-invalid-tag class.
ScreenShots
1) Initially,
2) Typing 3 letters to get HTTP Call.
3) Now after selection of first Skill "Angular Js'.
4) It shows that .input.invalid-tag is enabled. And which doesn't clear the placeholder.
My Input Tag is as below.
<tags-input ng-model="employerMyCandidatesCtrl.skillList" placeholder="Skills..."
replace-spaces-with-dashes="false"
add-from-autocomplete-only="true"
display-property="skillName"
on-tag-added="employerMyCandidatesCtrl.addTagToSkillData($tag)"
on-tag-removed="employerMyCandidatesCtrl.removeTagFromSkillData($tag)">
<auto-complete
source="employerMyCandidatesCtrl.loadSkillData($query)"
displayProperty="skillName" debounce-delay="500"
min-length="3">
</auto-complete>
</tags-input>
Controller Code is as below.
vm.skillList = [];
vm.loadSkillData = function(query) {
return EmployerServices.getAllSkillsPromise(query); // $http call.
};
vm.addTagToSkillData = function(tag) {
if (_.findIndex(vm.skillList, tag) < 0) {
vm.skillList.push(tag);
}
};
vm.removeTagFromSkillData = function(tag) {
var ind = _.findIndex(vm.skillList, tag) > -1 ? vm.skillList.splice(ind, 1) : '';
};
Is any configuration mistake I'm doing?
There are 4 attributes for onTagAdding, onTagAdded, onTagRemoving, onTagRemoved so the basic difference between the attributes ending with adding compared to those ending with added is
Adding suffixed tags are expecting a boolean which when true will be added
or removed based on the tag used.
But onTagAdded/Removed already adds the tag, before the function is called hence we can do some additional logic or else strip the ng-model of the added value or add back the removed value(not very easy).
Check the below JSFiddle to see the four attributes in action here
I have made a custom service to supply the data, so the final answer to your question will be to use the appropriate attribute (onTagAdding, onTagAdded, onTagRemoving, onTagRemoved) based on your usecase. From the above code, I think we need not write onTagAdded, onTagRemoved since its done automatically.

issue with ngPattern

I am trying to design a nifty expiration date input on a credit card checkout form that will automatically insert a " / " between expiration month and year while the user is typing. The model no longer picks up the input value since I have introduced ngPattern validation to the input. Angular only allows a model to pick up the input value once the validation has succeeded. This basically makes my nifty feature not work due to my code. Can someone find a way around this. below is my code.
html
<input ng-keyup="checkout.updateExp()" class="form-control" type="text" maxlength="7" placeholder="mm / yy" required autocomplete="off" name="exp" ng-pattern="/\d{2}\s\/\s\d{2}/" ng-model="checkout.cf.exp">
controller function
vm.updateExp = function(){
var separator=" / ";
//add separator
if(vm.cf.exp.length==2){//-----> cannot process since ngPattern makes exp undefined till pattern is met
vm.cf.exp = vm.cf.exp.substring(0,2) + separator;
}
//remove separator
if(vm.cf.exp.length==4){
vm.cf.exp = vm.cf.exp.substring(0,1);;
}
};
Why not validate it manually using a regular expression instead of having it done using ng-pattern? You can set the $validity of the field manually just like angular would do it using ng-pattern.
In the html add
ng-keyup="checkout.updateExp(form.exp)" name="exp"
form.exp is the form and then the name of the input field. I do not know what the form name is so you will have to replace it accordingly.
vm.updateExp = function(formModel){
/* existing code omitted */
var expression = /\d{2}\s\/\s\d{2}/; // create RegEx
formModel.$setValidity("pattern", expression.test(vm.cf.exp)); // set validity to whatever name you want, I used the name pattern
};

ExtJS get value of hidden form field

First off, thanks for your patience I'm pretty new to Ext. All I'm trying to do is get the value of a hidden form field on an HTML page and store its value a variable inside an ext script. Here's what I'm working with:
HTML PAGE:
<form name="myForm">
<input type="hidden" id="accountID" name="divAccountID" value="463">
</form>
Ext Page:
var myAccountID = Ext.ComponentQuery.query('panel[name=myForm] #accountID');
This is most suitable:
var hidden = Ext.getCmp('accountID');
var hiddenValue = hidden.getValue();
You can get the value like that:
var v = Ext.get('accountID').dom.value;
Check this example: Jsfiddle
var win = Ext.widget('nameofviev');
//The above line will find the view in which we want to find the hidden fields
win.down('hidden#row_id').setValue(index);
//The above line set the value of hidden field that's `id` is `row_id`.
//If we remove the `#row_id` it find first hidden field and set value of that
// or
var hidden = Ext.getCmp('accountID');
var hiddenValue = hidden.getValue();

Resources